Engenharia de Software Moderna
Coletânea de Artigos Didáticos
Marco Tulio Valente
Versão 1.0 - janeiro 2023
Direitos autorais reservados. Esta versão é para uso pessoal e individual, sendo proibida
qualquer forma de redistribuição.
Conteúdo
Prefácio 1
1 Desafios da Formação de Engenheiros de Software 2
1.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2 Relevância . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3 Eixos de Formação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.4 Eixo Central de Formação . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.5 Eixo Transversal de Formação . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.6 Conclusão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2 Estudo de Caso: PIX no Banco X 8
2.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.2 Composição do Squad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.3 Como Começar? Workshop de Inception . . . . . . . . . . . . . . . . . . . . 10
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3 Organizando Times em Squads 11
3.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.2 O que é um Squad? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.3 Tipos de Squads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.4 Modelo Spotify . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4 Diagramas de Fluxo Cumulativo 16
4.1 Definição de CFD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
4.2 Para que serve um CFD? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
5 Shape Up: Uma Possível Alternativa a Scrum? 20
5.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
5.2 Principais Conceitos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
5.3 Fase de Shaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
5.4 Conclusão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
6 O que é e também o que não é um MVP? 25
6.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
6.2 Primeiro, o que é um MVP? . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
6.3 E o que não é um MVP? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
iii
CONTEÚDO iv
6.4 Pivôs vs Restarts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
6.5 Conclusão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
7 Product Discovery: Uma Breve Introdução 30
7.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
7.2 Exemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
7.3 Entrevistas com Clientes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
7.4 Outras Técnicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
7.5 Discovery com Scrum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
7.6 Discovery com Kanban . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
7.7 Conclusão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
8 Design Thinking: Principais Conceitos e Atividades 35
8.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
8.2 Principais Atividades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
8.3 Conclusão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
9 Jobs to be Done (JTBD) Aplicado a Produtos de Software 41
9.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
9.2 O que JTBD tem a ver com software? . . . . . . . . . . . . . . . . . . . . . . 42
9.3 Descobrindo os “Trabalhos” . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
9.4 Conclusão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
10 Mais Alguns Princípios de Projeto 46
10.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
10.2 CQS: Command-Query Separation . . . . . . . . . . . . . . . . . . . . . . . 46
10.3 DRY: Don’t Repeat Yourself . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
10.4 YAGNI: You Aren’t Gonna Need It . . . . . . . . . . . . . . . . . . . . . . . 48
10.5 KISS: Keep It Simple, Stupid . . . . . . . . . . . . . . . . . . . . . . . . . . 48
10.6 Ortogonalidade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
11 dulos devem ser Profundos! 50
11.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
11.2 dulos Profundos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
11.3 dulos Rasos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
12 Domain-Driven Design (DDD): Um Resumo 54
12.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
12.2 Linguagem Ubíqua . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
12.3 Objetos de Domínio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
12.4 Contextos Delimitados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
12.5 Camada Anticorrupção . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
12.6 Conclusão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
CONTEÚDO v
13 Criando Objetos Compostos com o Padrão Composite 62
13.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
13.2 Contexto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
13.3 Problema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
13.4 Solução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
13.5 Conclusão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
14 O que é Injeção de Dependência? 66
14.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
14.2 Frameworks para Injeção de Dependência . . . . . . . . . . . . . . . . . . . . 67
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
15 Construindo Sistemas com uma Arquitetura Limpa 70
15.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
15.2 Entidades e Casos de Uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
15.3 Adaptadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
15.4 Frameworks Externos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
15.5 Regra de Dependência . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
15.6 Invertendo o Fluxo de Controle . . . . . . . . . . . . . . . . . . . . . . . . . 72
15.7 Conclusão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
16 O que é uma Arquitetura Hexagonal? 76
16.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
16.2 Adaptadores e Portas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
16.3 Exemplo: Sistema de Bibliotecas . . . . . . . . . . . . . . . . . . . . . . . . 78
16.4 Conclusão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
17 Consistência de Dados em Microsserviços usando-se Sagas 81
17.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
17.2 Garantindo Atomicidade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
17.3 Sagas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
18 O que é uma Arquitetura Serverless? 85
18.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
18.2 Histórico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
18.3 Modelo de Cobrança . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
18.4 Funções Serverless . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
18.5 Conclusão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
19 Alguns Exemplos Reais e Interessantes de Testes 90
19.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
19.2 Guava . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
19.3 Spring PetClinic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
19.4 JUnit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
19.5 Vue.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
CONTEÚDO vi
19.6 Microsoft PowerToys . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
20 Duas Recomendações para Manutenção de Software 98
20.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
20.2 Teoria das Janelas Quebradas . . . . . . . . . . . . . . . . . . . . . . . . . . 98
20.3 Regra dos Escoteiros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
21 Revisão de digo: Uma Breve Introdução 101
21.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
21.2 Pull Requests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
21.3 Motivações . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
21.4 O que Revisar? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
21.5 Recomendações para Revisores . . . . . . . . . . . . . . . . . . . . . . . . . . 104
21.6 Recomendações para Autores . . . . . . . . . . . . . . . . . . . . . . . . . . 106
21.7 Automatizando a Revisão . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
22 Como Monetizar Sistemas de Software? 109
22.1 Licenças . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
22.2 SaaS: Software as a Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
22.3 Anúncios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
22.4 Open Source . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
22.5 Conclusão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
23 Microempresas de Software como Serviço 114
23.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
23.2 Exemplos de Micro-SaaS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
23.3 Escolhendo o Problema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
23.4 Canais de Distribuição . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
23.5 Outras Responsabilidades . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
23.6 Crescendo a Empresa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
23.7 Métricas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
Prefácio
Atualmente, sou responsável por duas disciplinas de graduação na UFMG: Engenharia de
Software e Prática em Desenvolvimento de Software.
A primeira é uma disciplina obrigatória para alunos de Ciência da Computação e ela tem
como objetivo oferecer uma visão abrangente da área de Engenharia de Software. Nela, eu
uso então o nosso livro texto (Engenharia de Software Moderna).
A segunda é uma disciplina de projeto na qual os alunos implementam, em grupos, um
sistema próximo do real, usando um método ágil e diversas das práticas que estudamos
na disciplina obrigatória, tais como histórias de usuários, testes automatizados, integração
contínua, etc. No entanto, eu sentia falta de também cobrir algum conteúdo “teórico” nessa
segunda disciplina, para que os alunos pudessem tomar contato com temas que não viram
na disciplina obrigatória. Assim, ainda durante a pandemia, surgiu a ideia de escrever esses
pequenos artigos didáticos, para que eles fossem o tema central de algumas aulas de Prática
em Desenvolvimento de Software. Para tornar essas aulas mais dinâmicas, os artigos incluem
também exercícios.
Adicionalmente, alguns dos artigos cobrem temas que considero emergentes em Engenharia
de Software, como novos métodos ágeis (por exemplo, Shape Up) e novas arquiteturas de
software (como serverless). Outros tratam de temas bastante comentados no mercado de
desenvolvimento, como Domain-Driven Design (DDD), Arquitetura Limpa e Arquitetura
Hexagonal. Ou seja, os artigos são um instrumento que também pensei para
manter o
leitor do livro seja ele um professor, aluno ou profissional do mercado sempre
atualizado
. Futuramente, em uma possível segunda edição, alguns desses artigos podem
então ser incorporados ao conteúdo “estático” do livro.
Todos os artigos estão disponíveis também no site:
https://engsoftmoderna.info/artigos/artigos.html
Nesta página, vo vai encontrar também os slides que uso nas minhas aulas.
Apesar de estarem online um bom tempo, eu acredito que a disponibilização agora
de todos os artigos em uma única coletânea pode facilitar a leitura linear e completa deles.
Então, boa leitura a todos!
Marco Tulio Valente
Belo Horizonte, 16 de janeiro de 2023.
1
Capítulo 1
Desafios da Formação de Engenheiros
de Software
1.1 Introdução
Em 2019, ministrei pela primeira vez um curso de Engenharia de Software para alunos
de graduação. Desde então, tenho estudado, trabalhado e refletido sobre questões ligadas
à formação qualificada de Engenheiros de Software no Brasil. Assim, neste artigo, vou
compartilhar minha visão sobre os desafios envolvidos nessa tarefa.
1.2 Relevância
Primeiro, para delimitar o contexto deste artigo, acho importante começar lembrando que
Engenharia de Software é cada vez mais essencial na formação de alunos de Ciência da
Computação, Sistemas de Informação, Engenharia de Computação, etc.
Para ressaltar esse fato, na primeira aula da minha disciplina apresento para os alunos o
seguinte diagrama.
Figura 1.1: Proporção de Engenheiros de Software versus Cientistas da Computação
Esclareço que ele não é baseado em nenhum estudo científico, mas na minha experiência
acadêmica e profissional.
Costumo usar também o seguinte tweet de um profissional com larga experiência no mercado
de desenvolvimento de software para reforçar o meu argumento:
2
CAPÍTULO 1. DESAFIOS DA FORMAÇÃO DE ENGENHEIROS DE SOFTWARE 3
A lot of people are excited about other tech disciplines than software engineering. But it’s
good to know ratios I’ve seen at Uber & similar places:PMs: 1 for every 10-20 engineersPMM:
1:30-100Data Scientist/analyist: 1:10-20Designer: 1:10-20UX researcher: 1:20-50
Gergely Orosz (@GergelyOrosz) June 8, 2021
Segundo o tweet, grandes empresas de tecnologia possuem de 10 a 20 mais engenheiros de
software do que Product Managers (PMs), analistas e cientistas de dados e designers de
interfaces, por exemplo.
Portanto, a mensagem para os alunos é clara: quando vocês se formarem, não todos, mas a
maioria será Engenheiro de Software. Ou terá um título equivalente, como Frontend, Backend,
Fullstack, Mobile developer, Arquiteto de Software, Tech ou Squad Lead, etc.
Contexto Brasileiro
Temos também no Brasil capacidade, vocação e potencial para desenvolver um mercado de
software relevante e com impacto global. Na verdade, não estou falando nenhuma novidade,
pois sempre se falou nesse assunto. Também, não estou sugerindo que nosso mercado de
software seja inexistente e inexpressivo. Pelo contrário, estamos avançando e temos inúmeros
casos de sucesso.
Por outro lado, temos esse descasamento paradoxal entre oferta e demanda de profissionais
de TI no Brasil. Digo, em um país onde existe um brutal problema de desemprego temos um
setor da economia empresas de TI com centenas de milhares de vagas abertas, sendo a
maioria delas para trabalho diretamente relacionado com Engenharia de Software.
Assim, acredito que nunca foi tão importante formar, no Brasil, um maior número de
profissionais com capacidade e interesse de atuar na área de Engenharia de Software.
1.3 Eixos de Formação
O propósito central deste artigo é destacar que a formação qualificada de Engenheiros de
Software requer um conjunto de esforços em dois eixos (veja também a próxima figura):
Eixo Central de Formação
: os desafios nesse eixo são de ordem
conceitual
e
prática.
Eixo Transversal de Formação
: aqui, os desafios envolvem, principalmente, um
domínio de
habilidades comportamentais
(soft skills) e também uma visão mesmo
que mais ampla de negócios.
Figura 1.2: Eixos de formação de Engenheiros de Software
A seguir, detalho um pouco mais sobre esses eixos e suas respectivas categorias de formação.
CAPÍTULO 1. DESAFIOS DA FORMAÇÃO DE ENGENHEIROS DE SOFTWARE 4
1.4 Eixo Central de Formação
Este eixo envolve a formação básica de um Engenheiro de Software, a qual deve contemplar,
em nossa opinião, o desenvolvimento de habilidades conceituais e práticas.
Formação Conceitual
Quando ministrei a disciplina de Engenharia de Software pela primeira vez, na graduação,
senti uma falta de livros didáticos mais modernos na área. Por isso, resolvi dedicar um ano
de intenso trabalho para escrever um livro-texto
1
sobre Engenharia de Software.
Sou suspeito para afirmar, mas acredito que, após a escrita desse livro, estou conseguindo
cobrir essa dimensão do ensino de Engenharia de Software de maneira satisfatória. Isso
envolve transmitir conceitos, práticas e princípios, sempre modernos, de processos, projeto,
arquitetura, testes, manutenção, etc de software.
Na verdade, estamos bastante satisfeitos com a adoção do livro. Após três anos de seu
lançamento, mais de 200 professores estão usando o nosso material.
Também gostaríamos de comentar sobre dois problemas que podem ocorrer quando aborda-se
a parte conceitual:
Ser muito abrangente e “conteudista” e, assim, procurar ensinar todo e qualquer método,
técnica ou ferramenta. Achamos que isso torna as aulas de Engenharia de Software
massantes e repetitivas.
Ser opinativo e, assim, emitir diversos comentários e vereditos sobre os temas estudados,
sem que eles estejam baseados em uma evidência empírica mais sólida.
Para evitar o primeiro problema, tomamos o cuidado de tornar o nosso livro enxuto (lean),
com cerca de 400 páginas. Mesmo em futuras edições, pretendemos manter esse tamanho e,
assim, evitar que o livro se transforme em uma enciclopédia. No fundo, a ideia é maximizar o
valor de cada página e, assim, manter o engajamento do leitor. Para nos ajudar a cumprir
esse objetivo, sempre lembramos da seguinte frase de Mark Twain:
Eu não tive tempo de escrever uma carta curta, então acabei
escrevendo uma carta longa . . .
No entanto, é importante ressaltar que extensões do livro estão em pleno andamento,
mas na forma de artigos didáticos
2
. Nesse caso, o propósito não é apenas apresentar um
conteúdo novo, mas também usar os artigos como trabalhos para os alunos. Esclareço: os
artigos são auto-contidos ou, no máximo, referenciam conteúdo do próprio livro. E são sempre
acompanhados de exercícios.
Formação Prática
Porém, de nada adianta dominar a teoria, se o aluno nunca colocou ela em prática. Ou seja,
ainda dentro do ambiente acadêmico e de sua estrutura curricular, é interessante que o aluno
possa aplicar os conceitos que viu na teoria.
Ao comentar sobre isso, sempre me lembro da frase do Linus Torvalds:
1
https://engsoftmoderna.info/
2
./artigos.html
CAPÍTULO 1. DESAFIOS DA FORMAÇÃO DE ENGENHEIROS DE SOFTWARE 5
Falar é fácil, mas quero ver o digo!
Então, sobre a dimensão prática da formação do Engenheiro de Software, quero dizer que não
concordo que ela possa ser completamente terceirizada por meio de estágios ou atividades
semelhantes.
Por outro lado, quando a universidade assume parte da responsabilidade pela formação prática
dos alunos, surgem complicações naturais do ambiente acadêmico. Por exemplo, torna-se
desafiador pedir que os alunos implementem um sistema real em uma disciplina acadêmica,
na qual devem ser cobertos também aspectos conceituais.
Da mesma forma, pedir para que os alunos trabalhem em sistemas de terceiros, como sistemas
de digo aberto, também não é trivial. O motivo é que o aluno, normalmente, não tem
tempo, no escopo de uma disciplina acadêmica, para entender um sistema real e complexo.
Assim, para atender à formação prática dos alunos, estamos trabalhando em duas frentes
principais:
Oferecendo para graduação, na UFMG, uma disciplina chamada
Prática de Desen-
volvimento de Software
, na qual metade da nota refere-se a um trabalho em equipe,
que requer o desenvolvimento de um sistema real (ou com características próximas de
um sistema real). Nesse trabalho, os alunos devem usar um método ágil e, portanto, têm
que escrever histórias de usuários, implementá-las em sprints, realizar alguns eventos,
como sprint reviews, implementar testes, etc. Durante a disciplina, eu, como professor,
tento assumir o papel de Product Owner, Scrum Master e consultor técnico dos times.
Estamos também iniciando o desenvolvimento de um conjunto de roteiros e programas
“ilustrativos”, de pequeno porte e que exercitam conceitos básicos de divisão processos,
arquitetura, devops, testes, etc.
1.5 Eixo Transversal de Formação
Neste eixo estão incluídas duas categorias de formação: comportamental e em negócios.
Decidimos chamar o eixo de transversal porque ele trata de questões que são também
importantes em outros profissionais de computação, como cientistas de dados, administradores
de redes, etc. Por isso, elas podem também ser abordadas em outras disciplinas de graduação.
Formação Comportamental
Concluindo e, na falta de um nome melhor, estou usando o termo “formação comportamental”
para referenciar os soft skills que são fundamentais em um Engenheiro de Software. Isto é,
hoje em dia, não basta saber programar e dominar os conceitos básicos da área (formação
conceitual). Não basta também ter capacidade de realização (formação prática).
Além disso, o Engenheiro de Software moderno tem que saber trabalhar em equipe, saber
liderar e ser liderado, saber se comunicar e se expressar, de forma escrita e oral, saber ouvir e
saber falar na hora certa, ser capaz de entender o que a organização espera dele, etc.
De fato, em um artigo
3
recente, publicado na revista científica Information and Software
Technology, fizemos uma análise de várias ofertas de emprego postadas no Stack Overflow
3
https://homepages.dcc.ufmg.br/~mtov/pub/2020-ist-skills.pdf
CAPÍTULO 1. DESAFIOS DA FORMAÇÃO DE ENGENHEIROS DE SOFTWARE 6
Jobs. Pudemos confirmar que a grande maioria faz referência a soft skills. Veja a seguir
aqueles que são mais demandados:
Figura 1.3: Soft skills demandados em Engenheiros de Software
Porém, a abordagem da dimensão comportamental em cursos de Engenharia de Software
é ainda um problema em aberto, pelo menos para mim. Talvez, na verdade, o problema
não seja exclusivo de disciplinas de Engenharia de Software, mas de caráter mais geral na
formação de profissionais de Computação.
Formação em Negócios
Em um mundo cada vez mais ágil e dinâmico, os Engenheiros de Software não podem restringir
sua atuação à escrita de digo. Eles têm também que constantemente refletir sobre o valor
que está sendo gerado pelo digo que eles produzem.
Para isso, os profissionais têm que entender do negócio principal da empresa na qual trabalham.
Isso é importante inclusive para conversar e argumentar com outros profissionais, como POs,
gerentes de produtos, designers e mesmo com os usuários finais.
No fundo, o emprego de Engenheiros de Software depende do valor gerado pelo digo que
eles produzem. Então, não é bom assumir que os requisitos e funcionalidades de um sistema
vão sempre chegar prontos e perfeitamente definidos. O desenvolvedor moderno tem que ser
capaz de propor novas funcionalidades, participar e influenciar na evolução dos sistemas nos
quais trabalha, sugerir novas funcionalidades e mesmo novas oportunidades de negócio.
Idealmente, um time de desenvolvedores deve procurar relacionar seu trabalho com a melhoria
de um indicador de negócio. Por exemplo, uma determinada funcionalidade pode estar sendo
implementada porque ela tem potencial para ampliar a base de clientes em x% ou reduzir a
taxa de cancelamentos em y%. Ela pode ainda permitir que a empresa economize z milhões
de reais em serviços de computação em nuvem ou em outros serviços.
Evidentemente, do ponto de vista de ensino, não para tratar da variedade de negócios que
beneficiam-se atualmente de software. Porém, pelo menos transversalmente, é importante
comentar com os alunos sobre a importância do alinhamento entre sistemas e negócios.
Outras disciplinas de graduação como uma disciplina de
empreendedorismo
podem
também ajudar nessa formação. Na verdade, em uma época de transformação digital como
CAPÍTULO 1. DESAFIOS DA FORMAÇÃO DE ENGENHEIROS DE SOFTWARE 7
a que estamos vivemos –, as oportunidades que existem para criação de novas empresas são
imensas. E Engenharia de Software é o motor da maioria dos processos de transformação
digital. Adicionalmente, as empresas cobram cada vez mais uma mentalidade empreendedora
de seus colaboradores, por meio do que costuma-se chamar de
empreendedorismo interno
.
1.6 Conclusão
Para concluir, gostaríamos de mencionar dois pré-requisitos que achamos fundamentais para
iniciar a formação de engenheiros de software:
É muito importante que o aluno goste de programar. Como fizemos questão de dizer no
prefácio do nosso livro “cada vez mais, engenheiros de software têm que escrever digo.
Hoje, pouco espaço para dizer que eu não preciso programar, pois sou arquiteto ou
analista.
É muito importante também que o aluno saiba programar. Ou seja, o aluno deve ter
domínio de conceitos básicos de programação, algoritmos e estruturas de dados.
Capítulo 2
Estudo de Caso: PIX no Banco X
2.1 Introdução
Neste artigo, iremos apresentar um estudo de caso sobre a organização de um time ágil ou
squad
1
que segue um método semelhante a Scrum
2
. Nosso objetivo é explicar a composição
desse squad hipotético, isto é, comentar sobre os principais profissionais que fazem parte dele
e sobre as suas responsabilidades e tarefas.
Contexto:
Vamos assumir que o squad trabalha em um banco hipotético, chamado Banco
X. Após a regulamentação do PIX pelo Banco Central, o Banco X deseja implementar e
disponibilizar esse serviço de transferência online. E nosso squad ficou responsável por essa
implementação, que ele cuida dos demais tipos de transferência do banco, tais como TED
e DOC.
Importante:
objetivo deste artigo é totalmente didático. Portanto, o Banco X e o nosso
squad são hipotéticos. Além disso, fizemos algumas simplificações para tornar o exemplo
compacto e mais fácil de entender.
2.2 Composição do Squad
Vamos assumir que o squad de exemplo é formado por:
1 Project Owner (PO)
1 UI/UX Designer
1 Desenvolvedor Frontend
1 Desenvolvedor Mobile
2 Desenvolvedores Backend
1 Scrum Master
A seguir comentamos sobre as responsabilidades e tarefas desses profissionais, sempre no
contexto da implementação do nosso serviço de PIX no Banco X.
1
./squads.html
2
../cap2.html#scrum
8
CAPÍTULO 2. ESTUDO DE CASO: PIX NO BANCO X 9
Product Owner (PO)
Às vezes chamado também de Product Manager (PM)
3
, cabe a esse profissional estudar e
entender os requisitos para operação do PIX, definidos pelo Banco Central. Para isso, o PO
conta com o apoio das áreas de negócio do banco, incluindo analistas financeiros, analistas
bancários, contadores, pessoal de marketing, growth, etc.
Em seguida, o PO deve explicar para o time como o PIX funciona e “o que” deve ser
implementado. Deve também explicar o cronograma do projeto, pois existe uma data para o
PIX entrar no ar.
Adicionalmente, deve propor e escrever as principais histórias de usuários
4
e também, muito
importante, definir os testes de aceitação das mesmas.
Por fim, deve tirar dúvidas dos desenvolvedores durante a implementação do projeto.
UI/UX Designer
Esse profissional também deve entender bem os requisitos do PIX. Uma fez feito isso, ele
deve observar e entrevistar possíveis usuários, incluindo clientes do banco e, talvez, aqueles
que ainda não são clientes.
Como resultado, ele deve criar um protótipo de interface com o usuário (UI), usando mockups,
wireframes, etc e ferramentas como Figma. Por fim, deve testar e validar esses protótipos,
sempre com usuários.
Desenvolvedores Frontend e Mobile
Esses desenvolvedores vão implementar a interface projetada pelo UI/UX designer. Para isso,
podem usar frameworks como React (no caso da versão Web) e React Native ou Flutter (no
caso da versão mobile).
Eles podem também sugerir e participar de testes com usuários, os quais serão mais uma vez
acompanhados pelo UI/UX designer. E, não menos importante, podem implementar testes
end-to-end
5
, usando frameworks como o Cypress.
Desenvolvedores Backend
Esses desenvolvedores vão implementar a “lógica” do PIX no Banco X, incluindo regras de
negócio, persistência em bancos de dados, protocolos de comunicação com o Banco Central e
outros bancos, etc.
Eles devem pensar e garantir que essa implementação atende a requisitos não-funcionais
6
,
tais como segurança, privacidade, disponibilidade, desempenho, etc. E também implementar
testes de unidade
7
e de integração
8
.
3
./processos-faq.html#qual-a-diferen%C3%A7a-entre-product-owner-po-e-product-manager-pm
4
../cap3.html#hist%C3%B3rias-de-usu%C3%A1rios
5
../cap8.html#testes-de-sistema
6
../cap3.html#introdu%C3%A7%C3%A3o
7
../cap8.html#testes-de-unidade
8
../cap8.html#testes-de-integra%C3%A7%C3%A3o
CAPÍTULO 2. ESTUDO DE CASO: PIX NO BANCO X 10
Scrum Master
Às vezes chamado de Agile Master ou Agile Coach, cabe a esse profissional acompanhar a
implementação do PIX e ajudar o time a seguir o processo de desenvolvimento do banco.
Ele deve organizar e facilitar os eventos de um sprint, tais como reuniões de planejamento,
reuniões diárias, revisão e retrospectiva. E, sempre, atuar como um líder servidor
9
, ajudando
o time a remover impedimentos não-técnicos que, inevitavelmente, vão surgir durante a
implementação.
Outros Profissionais
Além daqueles mencionados, outros profissionais podem contribuir com a implementação do
PIX no Banco X, incluindo:
Engenheiro de Infraestrutura (ou DevOps
10
): cabe a esse profissional definir onde o
serviço de transferência vai rodar. Por exemplo, na nuvem ou localmente. Ele deve
também garantir, planejar e cuidar para que o Banco X tenha infraestrutura adequada
para operar o serviço com segurança, desempenho, disponibilidade, etc.
QA (“Quality Assurance”): algumas empresas possuem profissionais dedicados para
garantia de qualidade. Eles podem ajudar o PO a escrever testes de aceitação e também
executar tais testes e validações ao final dos sprints.
2.3 Como Começar? Workshop de Inception
Antes de começar a rodar os sprints, pode-se realizar um workshop com todos os membros do
time e alguns stakeholders importantes do projeto. Em um projeto dessa complexidade, esse
workshop normalmente chamado de inception pode durar alguns dias.
Os principais objetivos do encontro são os seguintes: definir o escopo e cronograma do projeto;
definir as principais tarefas e histórias de usuários; identificar e mapear riscos e obstáculos;
criar a primeira versão do backlog do produto
11
e definir os objetivos dos primeiros sprints.
Terminado o workshop de inception, os sprints podem começar!
Exercícios
1. Escreva três histórias de usuários do projeto PIX no Banco X.
2. Suponha que o projeto PIX no Banco X foi concluído com sucesso. Meses depois, o Banco
Central baixou a seguinte regra: transferências via PIX entre 20:00 e 06:00 devem obedecer
ao limite de R$ 1.000,00. Qual seria o papel de cada membro do time na implementação
dessa nova regra? Descreva de forma simplificada.
9
./processos-faq.html#em-scrum-o-que-%C3%A9-um-l%C3%ADder-servidor
10
../cap10.html
11
../cap2.html#principais-artefatos-e-eventos
Capítulo 3
Organizando Times em Squads
3.1 Introdução
Neste artigo, iremos começar explicando o que é um squad. Em seguida, iremos apresentar os
principais tipos de squads que existem em empresas de software. Por fim, vamos concluir
apresentando o famoso Modelo Spotify, que define um modelo matricial para organização
de empresas de software em squads, mas que também inclui outras estruturas, como tribos,
chapters e guildas.
3.2 O que é um Squad?
Squads são os times ágeis, isto é, times multidisciplinares, auto-organizáveis e autônomos. Ti-
picamente, um squad tem menos de 10 profissionais, que seguem um determinado método ágil.
Por exemplo, eles podem realizar reuniões de planejamento, reuniões diárias, retrospectivas e
revisões, no caso de adotarem Scrum.
Um squad deve possuir autonomia para colocar suas histórias de usuário em produção. Na
verdade, esse é um objetivo central do conceito: criar condições para que cada time funcione
como uma microempresa com bastante autonomia dentro da organização. Para que isso seja
possível, é importante minimizar as dependências entre squads.
Portanto, em uma organização baseada em squads não existem silos funcionais, tais como o
time de banco de dados, o time de qualidade, o time de testadores, o time de implantação
(deployment), etc. Em vez disso, esses profissionais com diferentes formações e responsa-
bilidades estão distribuídos pelos diferentes squads, que assim transformam-se em células
autônomas de desenvolvimento, manutenção e entrega de software.
3.3 Tipos de Squads
Em organizações que desenvolvem software, podem existir os seguintes tipos de squad:
Squads de produto
Squads responsáveis por componentes
Squads responsáveis por plataformas
Squads facilitadores de boas práticas
Squads responsáveis por objetivos de negócio
Iremos explicar cada um deles a seguir.
11
CAPÍTULO 3. ORGANIZANDO TIMES EM SQUADS 12
Squads de produto
Squads de produto são o
tipo mais comum de squad
. Como o nome sugere, esses squads
são responsáveis por funcionalidades específicas do produto de software que está sendo
desenvolvido pela organização. Por exemplo, em uma loja online, podem existir squads
responsáveis pelo processo de compra (checkout), pelo pagamento, pela recomendação de
produtos, pelo catálogo de produtos, etc.
Para tornar sua missão mais clara, alguns squads de produto são responsáveis por personas
específicas do sistema. Por exemplo, um sistema com três personas X, Y e Z pode ser
desenvolvido por três squads de produto, sendo cada um deles responsável por uma persona.
No livro Team Topologies, os autores Manuel Pais e Matthew Skelton optam por chamar tais
squads de
times alinhados a fluxos de negócio
(stream-aligned teams). Independente-
mente do nome, o objetivo é o mesmo: esses squads têm como missão resolver problemas que
facilitam a vida dos clientes e que, ao mesmo tempo, geram valor para a organização.
Conforme afirmamos, squads de produto são o tipo mais comum de squad. Segundo Pais
e Skelton, a proporção de squads de produto em relação aos demais squads costuma variar
entre 6:1 (isto é, de cada sete squads, seis são de produto, enquanto um não é) até 9:1 (
nove
squads de produto, para um squad de um outro tipo).
Squads responsáveis por componentes
Esses squads são responsáveis por componentes específicos da arquitetura do sistema. O
exemplo clássico são squads responsáveis pelos clientes veis de um sistema, incluindo
Android, iOS, etc. Como um segundo exemplo, em empresas menores, podemos ter squads
para componentes front-end e back-end.
Os autores de Team Topologies também usam um nome diferente para tais squads, optando por
chamá-los de
times responsáveis por subsistemas complexos
(complicated-subsystem
teams), isto é, subsistemas ou componentes cujo desenvolvimento requer um conhecimento
específico. Como exemplo, podemos mencionar subsistemas para processamento de vídeos,
subsistemas que usam modelos matemáticos sofisticados, subsistemas para detecção de fraudes,
etc. Porém, eles afirmam que tais times são opcionais e somente devem ser usados quando for
claramente necessário.
Squads responsáveis por plataformas
Esses squads desenvolvem ferramentas e plataformas internas que são usadas pelos demais
squads. Ou seja, são squads que implementam serviços para os outros squads da organização.
Por exemplo, eles podem desenvolver ferramentas para facilitar a instalação do sistema na
máquina local dos desenvolvedores, para facilitar o deployment do sistema em serviços de
cloud, para implementar serviços de autenticação, segurança, logging ou monitoramento, para
realizar testes A/B, etc.
Em última instância, o objetivo é liberar os demais squads das preocupações relacionadas
com a implementação de tais funcionalidades técnicas, ou seja, não diretamente relacionadas
com o negócio.
CAPÍTULO 3. ORGANIZANDO TIMES EM SQUADS 13
Squads facilitadores de boas práticas
Em organizações maiores, podem existir squads cuja finalidade é prestar consultoria e
capacitação para os demais times da organização sobre boas práticas para desenvolvimento de
software, incluindo automação de testes, implementação de front end, qualidade de software,
arquitetura, performance de bancos de dados, etc. Eles costumam ser chamados também de
times facilitadores (enabling teams).
Adicionalmente, esses squads podem prospectar e recomendar novas tecnologias para a
empresa. E também recomendar a aposentadoria de algumas tecnologias ou a atualização da
versão de outras.
Squads responsáveis por objetivos de negócio
Esses squads miram em um problema específico que a empresa esteja enfrentando. Por
exemplo, pode-se criar um squad para melhorar a retenção de clientes de um sistema SaaS
(Software as a Service). As soluções desenhadas e implementadas por esse squad podem
incluir programas de fidelidade, cupons de desconto, etc. Como um segundo exemplo, um
squad pode ficar responsável por implantar o produto de software em um outro país.
Desenvolvedores são maioria nos tipos anteriores de squads. em squads voltados para um
objetivo de negócio existe um maior equilíbrio entre desenvolvedores e outros papéis, como
gerentes de produto, vendedores, pesquisadores de UX, analistas de marketing, cientistas de
dados, etc.
3.4 Modelo Spotify
O Modelo Spotify define uma organização para times ágeis, principalmente quando uma
empresa cresce e passa a ter dezenas ou centenas de squads. Quando isso aconteceu na Spotify,
eles adotaram uma estrutura organizacional em forma de matriz, ilustrada na seguinte figura:
Figura 3.1: Figura reproduzida do artigo Scaling Agile @ Spotify with Tribes, Squads,
Chapters & Guilds. Henrik Kniberg & Anders Ivarsson, 2012.
Nesse modelo matricial temos dois tipos principais de estruturas:
CAPÍTULO 3. ORGANIZANDO TIMES EM SQUADS 14
Estruturas verticais, representadas pelos squads e, também, por tribos.
Estruturas horizontais, representadas por chapters e guildas.
A seguir, iremos detalhar cada uma dessas estruturas (com exceção de squads, que foram
explicados antes).
Tribos
Tribos são conjuntos de squads que trabalham em sistemas ou subsistemas semelhantes. Toda
tribo possui um líder, responsável por garantir que os squads estão trabalhando de forma
coordenada e de acordo com as prioridades do negócio. Recomenda-se que uma tribo tenha no
máximo 100 membros. Se esse limite for ultrapassado, deve-se criar mais um nível, constituído
por Alianças ou Unidades de Negócio, isto é, conjuntos de tribos.
Chapters
Chapters são estruturas que agrupam, de forma horizontal, profissionais que possuem as
mesmas competências. Por exemplo, podem existir chapters de desenvolvedores front-end, de
cientistas de dados, de especialistas em UI/UX, etc. Esses profissionais no dia a dia estão
espalhados pelos squads. Porém, com uma frequência definida, eles se reúnem para trocar
ideias, definir práticas comuns, etc. Como mostrado na figura acima, os chapters congregam
profissionais de uma mesma tribo. Eles possuem um líder formal, que é responsável junto
com o setor de RH da empresa por questões como contratação, treinamento e promoções.
Guildas
Guildas são comunidades informais e, às vezes, temporárias. Isto é, são grupos de funcionários
que se reúnem, voluntariamente, para compartilhar interesses comuns, como uma tecnologia,
arquitetura, processo, etc. Os participantes de uma guilda não precisam pertencer à mesma
tribo.
O conceito de guilda existia antes do Modelo Spotify ser proposto, com nomes como
comunidade de prática ou grupos de interesse.
Adoção do Modelo Spotify
O Modelo Spotify foi proposto em 2012. Em 2014, dois vídeos
1
, com o formato de animação,
viralizaram e ajudaram a tornar o modelo conhecido.
Desde essa época, a adoção do modelo, mesmo na Spotify, passou por evoluções e adaptações
importantes. Isso é reconhecido, inclusive, pelos próprios autores do artigo que descreve o
modelo:
A Spotify (como qualquer boa empresa ágil) está evoluindo ra-
pidamente. Este artigo é apenas uma fotografia de nossa forma
atual de trabalhar uma jornada em andamento, não uma jornada
concluída. Quando vo estiver lendo o artigo, provavelmente as
coisas mudaram.
1
https://blog.crisp.se/2014/03/27/henrikkniberg/spotify-engineering-culture-part-1
CAPÍTULO 3. ORGANIZANDO TIMES EM SQUADS 15
Por isso, como é normal quando o assunto são processos, a adoção de squads, tribos, chapters
e guildas não deve ocorrer cegamente de acordo com o manual. Adaptações ao contexto serão
sempre necessárias.
Exercícios
1. Para fixar as principais ideias do artigo, responda ao seguinte exercício
2
de V ou F, com
correção online.
2. Descreva um problema de estruturas baseadas em silos funcionais, isto é, times separados
de desenvolvedores, administradores de bancos de dados, testadores, operação, designers, etc.
3. No artigo sobre a adoção de squads na Spotify, são citados os seguintes exemplos de squads:
(a) para implementar o cliente Android do Spotify; (b) para criar uma “experiência de rádio”
ao usar o Spotify; (c) para escalar os componentes de backend da arquitetura do Spotify; (d)
para prover funcionalidades de pagamento. Como você classificaria cada um desses squads de
acordo com os tipos de squads que comentamos neste artigo.
4. Suponha uma organização que organiza seus squads em componentes arquiteturais.
Especificamente, ela possui squads que implementam componentes front-end e outros squads
que implementam componentes back-end. Discorra sobre o principal problema desse tipo de
organização de squads.
5. Qual a principal semelhança entre squads de plataformas e squads facilitadores de boas
práticas? E também qual é a diferença principal entre esses squads?
6. Suponha que vo trabalha em uma empresa organizada em squads, tribos, chapters e
guildas. Então, você possui “dois chefes” (ou líderes). Explique quais são esses líderes e qual
o papel de cada um deles.
7. Por que o Modelo Spotify é considerado uma estrutura matricial?
8. Descreva uma desvantagem do Modelo Spotify.
9. Suponha uma grande empresa de entrega de comida online. Descreva pelo menos três
tribos que essa empresa poderia usar para organizar seus squads. Basta descrever brevemente
o objetivo de cada tribo.
2
../exercicios/exvf.html#/cap/Squads
Capítulo 4
Diagramas de Fluxo Cumulativo
No Capítulo 2
1
, estudamos sobre Gráficos de Burndown e comentamos que eles podem ser
usados para acompanhar o fluxo de trabalho em um sprint, quando usa-se Scrum.
No entanto, Gráficos de Burndown não são adequados para times Kanban, pois eles assumem
que o objetivo é implementar um certo número de story points em um determinado período
de tempo, normalmente, equivalente a um sprint. E, quando usamos Kanban, não existem
mais sprints.
Por isso, times Kanban costumam usar um outro tipo de gráfico para acompanhar o seu fluxo
de trabalho, chamado de
Diagrama de Fluxo Cumulativo
(CFD, de Cumulative Flow
Diagram).
4.1 Definição de CFD
Um CFD é gerado a partir de uma tabela como a seguinte
Semana Especificação Implementação Revisão Pronto
1 10 10 9 4
2 9 8 9 13
3 10 10 10 22
4 8 10 10 32
5 10 9 9 42
. . . . . . . . . . . . . . .
onde:
Especificação, Implementação, Revisão e Pronto são as colunas do Quadro Kanban
usado pelo time.
Em cada linha, mostra-se o número de histórias de usuários que estão em cada coluna
no final da respectiva semana. Por exemplo, no final da Semana 1, existiam 10 histórias
em Especificação, 10 histórias em Implementação, 9 histórias em Revisão e 4 histórias
Prontas.
1
../cap2.html#principais-artefatos-e-eventos
16
CAPÍTULO 4. DIAGRAMAS DE FLUXO CUMULATIVO 17
E, muito importante, assume-se que as histórias ou, se você quiser, os post-its não
saem do quadro. Em vez disso, elas vão se acumulando na coluna Pronto. Daí o nome
cumulativo
do diagrama. Por exemplo, da semana 1 até a semana 5, um total de 42
histórias foram finalizadas pelo time.
A partir dessa tabela gera-se um gráfico de áreas empilhadas, que é chamado de
Diagrama
de Fluxo Cumulativo
(CFD, de Cumulative Flow Diagram). Veja o CFD gerado a partir
da tabela anterior:
Figura 4.1: Exemplo de CFD
Em um CFD, a faixa inferior corresponde à coluna mais à direita do quadro Kanban. No nosso
exemplo, a coluna Pronto. A partir dela, são empilhadas as demais colunas, da direita para a
esquerda do quadro. Assim, a última coluna empilhada, no nosso exemplo, é Especificação.
Para facilitar o entendimento, segue uma segunda versão do CFD anterior, agora com labels
indicando o valor de cada ponto do gráfico:
Ele está disponível também para acesso online no Google Sheets, neste link
2
.
No exemplo, consideramos que o eixo y de um CFD é o número de histórias em cada coluna de
um Quadro Kanban, em cada semana. No entanto, dependendo do contexto, pode-se adotar
outras unidades, como número de tarefas, bugs, itens de trabalho, story points, etc. De forma
semelhante, no eixo x podemos usar uma outra unidade de tempo, como dias, quinzenas,
meses, etc.
Além disso, neste artigo, para a explicação ficar mais simples, não vamos quebrar as histórias
em tarefas menores no passo de Especificação, tal como fizemos na seção sobre Kanban no
Capítulo 2
3
. Ou seja, imagine que as histórias são simples e podem ser Implementadas e
Revisadas sem necessidade de divisão em tarefas menores.
2
https://docs.google.com/spreadsheets/d/1cJpV
_
R2xnhXxSpVWYc7ZY0
_
o069Ano9oMc-VcK6PDLo
/edit?usp=sharing
3
../cap2.html#kanban
CAPÍTULO 4. DIAGRAMAS DE FLUXO CUMULATIVO 18
Figura 4.2: Exemplo de CFD com labels de dados
4.2 Para que serve um CFD?
Um CFD é usado para indicar se o fluxo de trabalho de um time Kanban está
estável
ou não.
Especificamente, o fluxo está normal e estável quando as áreas do diagrama crescem de forma
linear, com praticamente a mesma inclinação, como no CFD mostrado na seção anterior.
Por outro, quando existem problemas em um passo, a sua área no diagrama tende a crescer,
como mostrado no seguinte exemplo (acesse também neste link
4
):
Figura 4.3: Sistema instável, com acúmulo crescente de histórias em Implementação
4
https://docs.google.com/spreadsheets/d/1cJpV
_
R2xnhXxSpVWYc7ZY0
_
o069Ano9oMc-VcK6PDLo
/edit#gid=882423560
CAPÍTULO 4. DIAGRAMAS DE FLUXO CUMULATIVO 19
Nesse CFD, o passo de implementação está ocupando, gradativamente, uma área maior no
diagrama, o que significa que as histórias estão se acumulando nesse passo. Consequentemente,
menos histórias estão ficando prontas, ou seja, a inclinação da linha “Pronto” está diminuindo.
Podemos observar também que menos histórias estão sendo revisadas, pois a faixa vermelha
praticamente sumiu. E, por fim, o acúmulo de histórias em Implementação começa a gerar
um represamento também em Especificação.
Para Saber Mais
CFDs são tratados na maioria dos livros sobre Kanban. Especificamente, o livro “Diagrama
de Fluxo Cumulativo”, de autoria de Paulo Caroli, oferece uma introdução objetiva ao assunto.
O livro do David Anderson Kanban: Successful Evolutionary Change for Your Technology
Business é uma outra referência importante sobre CFDs.
Exercícios
1. Neste artigo, comentamos que Diagramas de Fluxo Cumulativo (CFD) são muito usados
com Kanban. Mas é possível usar CFDs também com Scrum? Justifique sua resposta.
2. Em um CFD estável, a taxa de chegada de novas histórias é igual à taxa com que histórias
são puxadas para o próximo passo. Verdadeiro ou Falso? Justifique.
3. Suponha um processo de desenvolvimento do tipo Waterfall, com três fases: Especificação,
Implementação e Testes. Crie um CFD para um projeto desenvolvido nesse processo. No
seu CFD, assuma um sistema cujo desenvolvimento inclui 25 funcionalidades. As fases
Especificação, Implementação e Testes levam, respectivamente, 2, 3 e 1 semestre.
4. Use um software de planilhas para criar CFDs hipotéticos, tal como fizemos neste
artigo. Defina um horizonte de tempo (eixo x) e alguns passos (colunas da planilha e,
consequentemente, áreas do diagrama). Em seguida, lance valores hipotéticos na sua planilha.
Crie pelo menos dois CFDs: um CFD representando um fluxo de trabalho estável e também
um CFD com instabilidade em algum passo do quadro.
Capítulo 5
Shape Up: Uma Possível Alternativa
a Scrum?
5.1 Introdução
Shape Up é um novo método para desenvolvimento de software que está sendo proposto pela
Basecamp
1
, a empresa por trás do software de gerenciamento de projetos de mesmo nome. O
método é descrito em um livro
2
aberto, de autoria de Ryan Singer. Segue uma imagem da
capa do livro:
Figura 5.1: Capa do livro sobre Shape Up
5.2 Principais Conceitos
Para apresentar Shape Up, vamos usar algumas analogias com outros métodos ágeis, principal-
mente com Scrum. Se quiser saber um pouco mais sobre métodos ágeis, você pode consultar
o Capítulo 2
3
do livro.
1
https://basecamp.com/
2
https://basecamp.com/shapeup
3
../cap2.html
20
CAPÍTULO 5. SHAPE UP: UMA POSSÍVEL ALTERNATIVA A SCRUM? 21
Um ciclo de desenvolvimento em Shape Up é composto por três etapas:
Primeiro, temos uma fase de projeto up front, chamada de
shaping
. Claro, esse projeto
é mais simples do que em Waterfall (isto é, não é um big design up front). Porém, a
ideia é que todo projeto de desenvolvimento de software precisa de uma coordenação
de mais alto nível, que defina as funcionalidades que serão implementadas, crie limites
para elas e avalie os seus riscos e benefícios. Tudo isso é feito nessa fase de shaping.
Como ela é central no método e nome para ele iremos comentar mais sobre ela
daqui a pouco, em uma seção separada.
Depois da fase de shaping começa um sprint propriamente dito, que eles chamam de
ciclos
. Porém, esses ciclos são mais longos do que os sprints de Scrum, tendo uma
duração de seis semanas. Segundo o autor, seis semanas é um tempo suficiente para
implementar algo relevante, mas sem impor um forte deadline aos trabalhos do time.
Finalizado um ciclo, segue um período de calmaria, de duas semanas, que os autores
chamam de período de
cool-down
. A ideia é que os desenvolvedores devem usar
esse período para respirar um pouco. E aproveitar para realizar outras tarefas, como
corrigir bugs, aplicar refactorings, pagar uma dívida técnica, estudar um nova tecnologia,
implementar uma prova de conceito, etc.
Por fim, em Shape Up, os times são menores do que os times de Scrum, sendo formados por
um designer de UI e, no máximo, dois desenvolvedores.
5.3 Fase de Shaping
Shaping é a fase principal do método e que nome a ele. A ideia é proporcionar uma solução
de meio termo, entre um planejamento detalhado (como em Waterfall) e um planejamento
mais vago e abstrato (como pode acontecer, por exemplo, quando adota-se Scrum). A próxima
figura ilustra o nível de detalhe que deve ser objeto da fase de shaping.
Figura 5.2: Fase de shaping
O livro não detalha os responsáveis pela fase de shaping, mas sugere que devem ser desenvol-
vedores e gerentes de nível sênior. Além disso, é importante mencionar que o shaping pode
CAPÍTULO 5. SHAPE UP: UMA POSSÍVEL ALTERNATIVA A SCRUM? 22
acontecer em paralelo com os ciclos de seis semanas e o período de cool-down. Por exemplo,
em paralelo com um determinado período de cool-down, realiza-se o shaping do ciclo seguinte.
O resultado da fase de shaping é um
pitch
, isto é, um documento simplificado que propõe uma
nova funcionalidade para um sistema. Esse documento deve incluir as seguintes informações:
Descrição do problema que espera-se resolver com a nova funcionalidade.
Quanto tempo pretende-se gastar com a implementação dessa funcionalidade. O autor
chama esse tempo de
apetite
. Veja então que uma funcionalidade pode demandar
menos de seis semanas (um ciclo). Pode-se, por exemplo, ter funcionalidades de duas
semanas.
Uma descrição em alto nível da solução proposta. Como o autor é originalmente um
designer, os exemplos mostrados no livro são descrições (ou sketches) de mais alto nível
de telas e interfaces.
Limitações e esclarecimentos. Na verdade, o autor usa o termo
no go’s
para limitações
que serão aceitas na solução a ser implementada. E o termo
rabbit holes
para designar
os impasses que podem ocorrer no momento da implementação (e então propor
soluções para eles na fase de shaping). O objetivo é evitar que o time se preocupe e
perca tempo com essas questões.
Por fim, gera-se um conjunto de pitches, que vão para o que o autor chama de
bet table
.
Basicamente, os pitches são distribuídos para um grupo de tomadores de decisão, que escolhem
e definem aqueles que de fato serão implementados no próximo ciclo. Essa decisão é tomada
de forma assíncrona, via troca de mensagens e discussões em uma ferramenta de gerência de
projetos (no caso, o próprio Basecamp). E, por fim, em uma curta reunião final e síncrona,
que deve durar poucas horas. No caso da Basecamp, as seguintes pessoas participam de uma
bet table: o CEO da empresa, o CTO da empresa, um desenvolvedor sênior e um estrategista
de produtos.
Terminada essa reunião, os pitches são distribuídos para os times. E começa-se o ciclo de seis
semanas.
Veja então que não existe um backlog de pitches, conforme comenta o autor:
If we decide to bet on a pitch, it goes into the next cycle to build.
If we don’t, we let it go. There’s nothing we need to track or
hold on to. What if the pitch was great, but the time just wasn’t
right? Anyone who wants to advocate for it again simply tracks it
independently—their own way—and then lobbies for it six weeks
later.
Ou seja, na verdade, o que não existe é um backlog global. Mas cada tomador de decisão
pode ter seu backlog local de pitches que ele pretende propor e defender na próxima bet table.
Por fim, Shape Up defende a ideia de “tempo fixo, mas escopo aberto”. Isto é, os ciclos têm
uma duração bem definida (por exemplo, 6 semanas). Porém, os times têm flexibilidade para
adaptar o escopo do que será, de fato, implementado no ciclo. Por isso, no início de um ciclo,
eles recebem um pitch, que é uma especificação de requisitos relativamente aberta e flexível.
CAPÍTULO 5. SHAPE UP: UMA POSSÍVEL ALTERNATIVA A SCRUM? 23
5.4 Conclusão
Shape Up tem diferenças importantes para métodos como Scrum:
Existe uma fase dedicada para planejamento e projeto (shaping).
Não existem eventos como reuniões diárias, revisões e retrospectivas.
Não existem artefatos como backlogs e quadros Scrum ou Kanban.
Os times possuem um tamanho mínimo: um designer e um ou dois desenvolvedores.
Não existem papéis semelhantes àqueles de Scrum Master e Product Owner. No entanto,
estrategistas de produto podem ser consultados para modelar os pitches e também
podem fazer parte da bet table.
Os times não têm autonomia para definir os pitches do próximo ciclo. Ou seja, eles
recebem os pitches prontos e modelados (shaped). Porém, os times continuam
autônomos para decidir como os pitches serão implementados no intervalo de seis
semanas.
Os times têm autonomia para adaptar o escopo de um pitch à duração prevista do
ciclo. em Scrum, o escopo do sprint é fixado logo no seu início, durante a reunião de
planejamento do sprint.
Ciclos são um pouco mais longos que os sprints de Scrum. Por exemplo, um ciclo pode
durar seis semamas, enquanto um sprint dura, no máximo, um mês e, frequentemente,
menos do que isso. Além disso, os ciclos são seguidos de um período de cool down, que
não envolve a implementação de novas funcionalidades.
Para Saber Mais
Se quiser saber mais sobre Shape Up, além do livro do Ryan Singer, recomendamos uma
palestra
4
que fizemos para o evento MPS Talks. Vo pode ouvir também diversas entrevistas
do Ryan Singer para podcasts. Os links estão na própria página do livro
5
. Por fim, o seguinte
vídeo
6
é uma excelente e breve introdução ao método.
Exercícios
1. Qual a diferença entre a fase de shaping e o planejamento do sprint (sprint planning) em
Scrum?
2. Em Shape Up, o que acontece quando um ciclo termina mas o time não consegue
implementar o que foi especificado no pitch? Para responder, consulte o Capítulo 8 do livro
7
que descreve o método, seção “The circuit breaker”.
3. Qual a principal característica que torna Shape Up um método adequado para times que
trabalham de forma remota?
4. O período de Cool Down existente em Shape Up não é uma novidade em métodos
ágeis. Por exemplo, em Extreme Programming (XP) existe uma prática parecida, mas com
4
https://youtu.be/CsgrTWtuBuE
5
https://basecamp.com/shapeup
6
https://youtu.be/h_8M23wVjXk
7
https://basecamp.com/shapeup/2.2-chapter-08#the-circuit-breaker
CAPÍTULO 5. SHAPE UP: UMA POSSÍVEL ALTERNATIVA A SCRUM? 24
um nome diferente. Qual é esse nome adotado em XP? Se necessário, consulte o Capítulo 2
8
do livro texto, seção 2.3.3.
5. Pense em um pitch para um determinado problema ou sistema. (a) Explique brevemente o
seu pitch (basta dar uma ideia geral); (b) Descreva um rabbit hole que deveria ser documentado
neste pitch.
8
../cap2.html#pr%C3%A1ticas-sobre-o-processo-de-desenvolvimento
Capítulo 6
O que é e também o que não é um
MVP?
6.1 Introdução
Produto Mínimo Viável (MVP)
é um conceito popularizado por Eric Ries no seu livro
Lean Startup. E é impressionante o sucesso e o alcance que ele alcançou. Hoje, o conceito é
usado por diversos empreendedores, sejam eles de produtos digitais ou mesmo de produtos
físicos. O conceito de MVP faz parte também do vocabulário de desenvolvedores de
software, tal como estudamos no Capítulo 3
1
.
No entanto, na área de software, o termo MVP é algumas vezes usado de uma forma que
não é totalmente fiel à sua definição original. Por isso, neste artigo didático, vamos tentar
esclarecer o significado mais preciso desse conceito.
6.2 Primeiro, o que é um MVP?
Para entender para que serve um MVP, temos que lembrar que o conceito foi proposto para o
contexto de startups, isto é, empreendimentos que operam em um ambiente de grande risco e
cujas chances de insucesso são enormes.
Em tais contextos, é importante testar rapidamente as hipóteses mais arriscadas do em-
preendimento. A
metodologia lean
para criação de startups defende que duas perguntas
fundamentais devem ser respondidas o quanto antes:
1.
Qual a hipótese mais arriscada do meu empreendimento? Isto é, qual a hipótese que se
não for verdade vai resultar no seu fracasso?
2.
Qual o menor experimento (isto é, MVP) que eu posso construir para validar essa
hipótese?
Normalmente, a primeira hipótese que deve ser testada é a capacidade da empresa atrair
clientes dispostos a pagar pelo seu produto ou serviço. Dizendo de outro modo, essa hipótese
pode ser enunciada da seguinte forma: existem clientes dispostos a pagar por uma solução X
para um problema Y?
1
../cap3.html#produto-m%C3%ADnimo-vi%C3%A1vel-mvp
25
CAPÍTULO 6. O QUE É E TAMBÉM O QUE NÃO É UM MVP? 26
Desse modo, o primeiro MVP de uma startup não costuma ser um sistema, pois o custo de
desenvolver um sistema mesmo que mínimo pode ser alto. Em vez disso, costuma-se
usar como primeiro MVP apenas uma landing page, um vídeo de demonstração ou sketches
de telas em papel. Ou seja, algo que pode ser construído em poucos dias. Feito isso, esse
primeiro MVP deve ser apresentado a potenciais clientes, para obter uma primeira ideia do
potencial de sucesso do empreendimento.
Por exemplo, suponha que seu primeiro MVP seja apenas uma página Web, descrevendo o
sistema que pretende desenvolver, suas principais funcionalidades e benefícios. Nessa página,
vo pode pedir que potenciais clientes deixem seu e-mail para receber notícias sobre o sistema
à medida que ele for ficando pronto. E então passa a valer a seguinte frase famosa sobre
metodologias lean:
Em um MVP, você decide o que é mínimo, mas é o cliente quem
decide se o produto é viável.
Ou seja, viável, na sigla MVP, significa um produto que atrai clientes em vez de um produto
que vo consegue implementar.
Continuando com o exemplo anterior, se ninguém se cadastrar na sua lista de mails, mesmo
após algum esforço de promoção, você deve avaliar se vale a pena continuar com a sua ideia.
Por outro lado, se os primeiros experimentos forem bem sucedidos, vo deve começar a
construir seus primeiros MVPs na forma de sistemas. Algumas recomendações nesse momento
são as seguintes:
Mesmo quando o MVP é um sistema, continue focando em adotantes iniciais (early
adopters), pois é mais difícil criar um produto para um grupo amplo e genérico de
usuários.
Passe a exigir algum pagamento, pois é importante validar se seus clientes estão dispostos
a pagar pelo sistema. Se eles não quiserem pagar, isso será uma ameaça à sobrevivência
do negócio.
Não tente automatizar tudo ainda. Como vo vai ter poucos clientes, várias tarefas
incluindo pagamentos, devoluções, cancelamentos, mudanças de planos de pagamento,
etc podem ser feitas de modo manual. Esse tipo de MVP é chamado de
MVP
Mágico de Oz.
Sempre reuse bibliotecas ou serviços de terceiros. Os exemplos clássicos são serviços de
autenticação de usuários e de cobrança por cartões de crédito.
Preste atenção também em alguns requisitos não-funcionais, como desempenho, usabili-
dade, segurança e privacidade. Esses requisitos se não tratados de forma adequada
podem causar o fracasso do seu projeto. Por fim, avalie se seu projeto possui algum
risco legal ou jurídico.
Por outro lado,
otimização prematura
é algo que deve ser evitado na construção de MVPs.
Veja, como o MVP pode falhar, não devemos nos preocupar em usar a última tecnologia da
moda. Em vez disso, tente usar linguagens e frameworks conhecidos e robustos. A seguinte
frase resume essa recomendação:
CAPÍTULO 6. O QUE É E TAMBÉM O QUE NÃO É UM MVP? 27
Ame o problema e não a solução ou tecnologia que vo está usando.
Escalabilidade é outro aspecto que não é tão importante. Se o MVP der certo, você terá
tempo e dinheiro para resolver problemas de escala. Da mesma forma, preocupações com uma
arquitetura de software extensível e com uma alta cobertura de testes não são fundamentais
neste momento inicial.
6.3 E o que não é um MVP?
Suponha agora que você seja contratado para fazer um sistema conhecido para uma certa
empresa. Por sistema conhecido estamos nos referindo a um sistema cujos requisitos são
claros e delimitados. Pode ser, inclusive, que você tenha feito um sistema parecido para
outras empresas. O importante aqui são dois pontos:
Não existe risco de o sistema à medida que ele for sendo desenvolvido se revelar
mais complexo e desafiador.
Não existe risco financeiro, pois o sistema tem um cliente que está pagando pelo seu
desenvolvimento.
Então, você resolve usar um método como Scrum
2
ou XP
3
para implementar esse sistema
(imagine, para ficar mais realista, que vo é dono de uma empresa de desenvolvimento e vai
contar com ajuda de alguns desenvolvedores nesse projeto).
Mais precisamente, o projeto será dividido em sprints de duas semanas. Assim, após o
primeiro sprint, você terá um sistema com alguma funcionalidade para mostrar para o
cliente.
A pergunta então é a seguinte: esse primeiro sistema é um MVP?
Não queremos ser dogmáticos e puristas quanto ao uso de termos, mas a resposta é
negativa
.
Conforme definimos, um MVP é um experimento para testar uma ameaça importante ao
sucesso de um projeto. No exemplo que acabamos de apresentar, no entanto, não existem
ameaças ou riscos relevantes. Conforme afirmamos: os requisitos e o escopo do sistema estão
claros, vo tem competência e experiência para desenvolvê-lo e o sistema tem um cliente
garantido, que vai pagar pelo seu desenvolvimento.
Por isso, os incrementos de produto produzidos nos sprints iniciais desse projeto não têm
características de um experimento, isto é, de um MVP. O seguinte tweet, de um consultor de
software, reforça e resume essa visão:
An MVP is an experiment that tests the hypothesis that something’s worth building. It is
NOT Version 1 of a product.
Allen Holub (@allenholub) January 6, 2022
6.4 Pivôs vs Restarts
Às vezes, também usamos o termo
pivô
de forma imprecisa. Para ficar claro, após criar um
MVP, recebemos feedback de potenciais clientes. Podem então ocorrer três situações:
2
../cap2.html#scrum
3
../cap2.html#extreme-programming
CAPÍTULO 6. O QUE É E TAMBÉM O QUE NÃO É UM MVP? 28
1.
O experimento foi bem sucedido e, por isso, alcançamos o que se chama de
product-
market fit. Vamos então começar a implementar um sistema de verdade.
2.
O experimento foi um fracasso! Portanto, talvez seja melhor desistir enquanto o prejuízo
é pequeno.
3.
Com o que aprendemos com o experimento, a gente consegue criar um novo MVP, dessa
vez com mais chances de sucesso. Ou seja, vamos realizar um pivô!
Em um pivô podemos realizar as seguintes mudanças:
Tentar atrair um outro tipo de cliente ou adotante inicial. Por exemplo, focar em
empresas em vez de pessoas físicas.
Realizar mudanças importantes nas funcionalidades do MVP. Por exemplo, o Instagram
começou como um aplicativo de check-in, chamado de Burbn, que permitia avisar seus
amigos que você estava em um determinado local e, então, compartilhar fotos com eles.
No entanto, essa última funcionalidade compartilhamento de fotos com amigos fez
tanto sucesso que se transformou em um novo produto, que os fundadores renomearam
para Instagram.
Migrar para uma nova tecnologia. Por exemplo, focar em aplicativos para celulares em
vez de um sistema Web.
Porém, não podemos confundir um pivô com um
restart
. Um restart ocorre quando os
fundadores abandonam, por completo, o propósito da empresa e recomeçam o empreendimento
do zero. Ou seja, um restart é uma desistência seguida por um recomeço em uma área nova.
Exemplo
: antes de criarem o Twitter, os seus fundadores trabalharam em uma plataforma
de distribuição de podcasts, chamada Odeo. Como a iniciativa não deu certo, eles desistiram
da Odeo e começaram a trabalhar em um novo sistema, dessa vez uma rede social para
compartilhamento de mensagens de texto com 140 caracteres, a qual chamaram de Twitter.
Figura 6.1: Exemplo de recomeço no caso do Twitter
Essa mudança completa de objetivo de um sistema para distribuição de podcasts para uma
rede social baseada em mensagens de texto não foi um pivô, mas um recomeço do zero.
CAPÍTULO 6. O QUE É E TAMBÉM O QUE NÃO É UM MVP? 29
6.5 Conclusão
Quando falamos de MVP, o mais importante não é o produto em si, mas a hipótese de risco
que procuramos testar. Se alguém comentar com você sobre um MVP, não se esqueça então
de perguntar sobre a hipótese de risco que está sendo testada.
Exercícios
1. Qual a diferença entre um MVP e uma pesquisa de mercado?
2. O seguinte artigo do blog da incubadora de startups YCombinador tem um título
interessante: A Minimum Viable Product Is Not a Product, It’s a Process
4
. Por que o artigo
defende essa tese? Isto é, por que, segundo o artigo, um MVP não é um produto, mas sim
um processo?
3. Nos seus primeiros dez anos de vida (1997-2007), a Netflix era uma empresa que entregava
DVDs fisicamente pelo correio, tendo nessa época centenas de milhares de assinantes.
Então, em 2007, a empresa migrou para o serviço de streaming, tal qual conhecemos e usamos
hoje. Vo classificaria essa mudança como um pivô? Sim ou não? Justifique e argumente a
favor de sua resposta.
4. Suponha que você planeja abrir uma empresa para entrega de comida pela Internet, em uma
cidade como Belo Horizonte, e pretende concorrer com empresas estabelecidas do ramo. A
criação de um MVP seria recomendada nesse contexto? Sim ou Não? Justifique e argumente
a favor de sua resposta.
5. Descreva um domínio no qual é mais desafiador criar um MVP. Justifique sua resposta.
4
https://www.ycombinator.com/library/4Q-a-minimum-viable-product-is-not-a-product-it-s-a-process
Capítulo 7
Product Discovery: Uma Breve
Introdução
7.1 Introdução
Em certos cenários, o sistema de software que devemos construir está bem definido e claro. Por
isso, nesses cenários, faz sentido usar a palavra
requisito
, pois o cliente consegue expressar
muito bem “o que” o sistema deve fazer.
No entanto, em outros cenários, essa clareza simplesmente não existe. Por exemplo, muitas
vezes não temos nem certeza de que o software ou alguma funcionalidade importante dele
será necessário e possuirá mercado. Então, nesse tipo de cenário, costuma-se usar o termo
discovery
para designar um conjunto de atividades realizadas com o objetivo de “descobrir”
o que de fato deve ser implementado.
Em cenários de discovery, costuma-se usar o termo
hipótese
, no lugar da palavra requisito.
O objetivo é deixar claro que primeiro precisamos realizar alguns experimentos para então
definir, de fato, o que será implementado.
Normalmente, o processo de discovery é liderado por profissionais como Product Owners
(PO), Product Managers (PM) ou UX Researchers. No entanto, é importante contar com a
participação também de desenvolvedores e outros stakeholders.
7.2 Exemplo
Suponha que trabalhamos em uma empresa que desenvolve um sistema de ensino a distância.
Os clientes são universidades que pagam uma assinatura mensal. Então, queremos implementar
uma nova funcionalidade nesse sistema: a possibilidade dos alunos marcarem reuniões síncronas
de curta duração com os professores das disciplinas para tirarem dúvidas individualmente.
A nossa hipótese é a seguintes:
30
CAPÍTULO 7. PRODUCT DISCOVERY: UMA BREVE INTRODUÇÃO 31
Reuniões síncronas vão melhorar a experiência de aprendizado dos
alunos. Consequentemente, o nosso sistema será melhor avaliado
pelos gestores das universidades. E, por isso, mais universidades
vão assinar o sistema e menos universidades vão solicitar o seu
cancelamento. Isso vai ajudar a atingir nossos principais objetivos
de negócio neste trimestre.
Então, para validar essa hipótese temos que verificar os seguintes itens:
Se a falta de uma interação síncrona e individual com os professores é uma “dor” real
que incomoda os alunos.
Se as universidades estão dispostas a alocar tempo de seus professores para participarem
dessas reuniões.
Se essa funcionalidade será viável economicamente. Por exemplo, teríamos que verificar
se as universidades poderiam cobrar por essas reuniões.
Se existe algum eventual problema legal relacionado com a implementação dessa nova
funcionalidade. Por exemplo, temos que verificar se o contrato de trabalho dos professores
permite esse tipo de atividade.
Se a nossa empresa possui capacidade técnica e recursos humanos, no momento, para
implementar essa nova funcionalidade. E também se conseguimos fazer isso em um
período de tempo razoável.
Uma vez que a hipótese tenha sido validada, ela deve ser transformada em uma ou mais
histórias de usuário. Por outro lado, caso ela não tenha se mostrado promissora, ela deve ser
descartada. Porém, pelo menos, não perdemos tempo implementando e disponibilizando uma
solução que não seria usada ou não iria agregar valor ao produto.
7.3 Entrevistas com Clientes
A principal técnica usada em processos de discovery são
entrevistas
com os clientes. Ou
seja, é importante ouvir os clientes do sistema para entender profundamente seus problemas,
hábitos e rotinas. E, assim, confirmar (ou não) se nossas hipóteses fazem sentido e, portanto,
vão agregar valor para eles.
No entanto, é importante evitar um erro comum nessas entrevistas: perguntar diretamente
para os clientes se nossa ideia é boa e se eles iriam usá-la, caso fosse disponibilizada no sistema.
Quando fazemos esse tipo de pergunta, o mais comum é receber uma resposta afirmativa,
mas apenas por uma questão de educação ou gentileza. Ou, simplesmente, porque os clientes
querem encerrar logo a conversa.
Em vez disso, uma entrevista deve focar nos problemas e rotinas dos clientes. E também focar
na experiência passada deles, em vez de perguntar como eles gostariam que fosse o futuro.
No sistema de exemplo, as perguntas poderiam ser as seguintes:
Qual foi a última vez que você precisou tirar uma dúvida importante com um professor?
Isso aconteceu em quais disciplinas?
Vo conseguiu tirar sua dúvida? Se sim, conte-nos como fez. Se não, houve algum tipo
de “prejuízo”?
CAPÍTULO 7. PRODUCT DISCOVERY: UMA BREVE INTRODUÇÃO 32
Vo costuma fazer perguntas no fórum do sistema de ensino a distância? Elas te
ajudam a tirar dúvidas?
Após as entrevistas e, caso nossas hipóteses tenham se mostrado promissoras, podemos dar
um próximo passo, que normalmente envolve a construção de alguns protótipos ou MVPs
1
.
7.4 Outras Técnicas
É importante mencionar também que, em certos cenários, nós não temos nenhuma ideia de
como resolver um certo problema. Ou seja, esses cenários são diferentes daquele do nosso
exemplo, no qual tínhamos uma funcionalidade em mente que pretendíamos implementar
no sistema.
Em tais cenários, nos quais as ideias e hipóteses não estão claras, podemos usar técnicas mais
abrangentes de discovery, tais como:
Design Sprints, que estudamos no Capítulo 3
2
.
Design Thinking, que tratamos no seguinte artigo didático
3
.
Jobs to be Done (JTBD), que também tratamos em um artigo didático
4
específico.
7.5 Discovery com Scrum
Para incluir atividades de discovery em Scrum, costuma-se adotar um sistema com duas
trilhas de trabalho (ou
dual track
), que rodam em paralelo, conforme mostrado na seguinte
figura.
Figura 7.1: Sistema de duas trilhas (dual track) com Scrum
1
https://engsoftmoderna.info/cap3.html#produto-m%C3%ADnimo-vi%C3%A1vel-mvp
2
../cap3.html#construindo-o-primeiro-mvp
3
./design-thinking.html
4
./jobs-to-be-done.html
CAPÍTULO 7. PRODUCT DISCOVERY: UMA BREVE INTRODUÇÃO 33
A primeira trilha inclui atividades de discovery, lideradas por POs e PMs, e seu objetivo é
descobrir histórias de usuários para o backlog do produto.
a segunda trilha inclui sprints de
delivery
, que são os sprints que seguem Scrum de forma
tradicional, conforme estudamos no Capítulo 2
5
. O objetivo dessa trilha é implementar e
entregar histórias de usuários.
7.6 Discovery com Kanban
Também podemos adaptar Kanban para incluir atividades de discovery. Normalmente, isso
envolve o uso de dois fluxos de trabalho:
Upstream Kanban
: esse é um novo fluxo (com seu respectivo quadro Kanban) que
é criado para incluir apenas atividades de discovery. Por exemplo, ele pode incluir
atividades para entender as demandas dos clientes e priorizá-las. Assim, somente vão
entrar no backlog do produto as tarefas que, de fato, devem ser implementadas pelo
time.
Downstream Kanban
: representa o fluxo principal de Kanban, tal como estudamos
no Capítulo 2
6
, que começa com o backlog e prossegue com os passos de implementação
das tarefas, tais como especificação, codificação e revisão.
Para ilustrar esses dois fluxos, costuma-se usar a imagem de um funil, como na seguinte
figura:
Figura 7.2: Upstream e Downstream Kanban
O Upstream Kanban corresponde à parte do funil que vai se estreitando para deixar claro
que nem toda demanda proposta pelos stakeholders precisa chegar ao backlog.
Achamos importante comentar também sobre a origem dos termos upstream e downstream.
Uma possível tradução seria montante (upstream) e jusante (downstream). Especificamente,
quando se constrói uma barragem em um rio, a parte do seu leito que foi represada, dando
origem ao repositório ou lago, chama-se montante do rio; a parte do rio após a barragem é
chamada de jusante.
5
../cap2.html
6
../cap2.html#kanban
CAPÍTULO 7. PRODUCT DISCOVERY: UMA BREVE INTRODUÇÃO 34
7.7 Conclusão
Vamos concluir com um frase de Martin Cagan, um autor conhecido na comunidade de
produtos digitais:
O objetivo de “product discovery” é separar rapidamente as boas
ideias das ideias ruins. O resultado esperado é um backlog do
produto com itens validados.
Exercícios
1. Qual problema pode ocorrer quando se adota Scrum ou Kanban (de forma tradicional) e
que atividades de discovery podem ajudar a resolver?
2. Durante o processo de discovery, ao entrevistar usuários, por exemplo, podemos ter novas
ideias, que vão gerar novas hipóteses. Descreva então uma nova hipótese de funcionalidade
para o exemplo de ensino a distância e que também tente solucionar um problema enfrentado
pelos alunos.
3. Além de ter sido feita com a mãe do entrevistador, qual o principal problema com a
seguinte entrevista?
Filho: Mãe, mãe, eu tenho uma ideia para um negócio posso
apresentá-la para você?
Mãe: Claro, querido.
Filho: Vo gosta do seu iPad, certo? Vo usa ele muito?
Mãe: Sim.
Filho: Então você compraria um aplicativo que fosse como um livro
de receitas para o seu iPad?
Mãe: Humm.
Filho: Deve custar apenas US$ 40 ou seja, mais barato do que
muitos livros de receita na sua prateleira.
Mãe: Bem. . .
Filho: E você vai poder compartilhar as receitas com seus amigos,
e também virá com um aplicativo para iPhone que permite criar
listas de compras. E vídeos daquele chef famoso que vo ama.
Mãe: Oh, sim querido, parece incrível. E vo está certo, US$ 40 é
um bom negócio. Terá fotos das receitas?
Filho: Sim, com certeza. Obrigado mãe te amo!
Fonte: exemplo extraído do livro The Mom Test: How to talk to customers & learn if your
business is a good idea when everyone is lying to you, de Rob Fitzpatrick.
4. Descreva três perguntas mais importantes que o entrevistador deveria ter feito na entrevista
anterior.
Capítulo 8
Design Thinking: Principais
Conceitos e Atividades
8.1 Introdução
Design Thinking é uma abordagem para solução de problemas não apenas de Engenharia
de Software, mas qualquer tipo de problema. A abordagem é inspirada em técnicas usadas
desde a década de 60 por designers de produtos, designers gráficos, designers de interface com
o usuário, etc.
Com o passar dos anos, os designers perceberam que essas técnicas poderiam ser usadas não
apenas para melhorar a ergonomia de um produto ou criar uma interface mais amigável, mas
também para resolver diversos outros problemas. Daí o nome
Design Thinking
, ou seja, o
objetivo é levar o pensamento dos designers para outras áreas.
Apesar de seus conceitos principais existirem desde os anos 60, Design Thinking tornou-se
popular a partir da década de 90 com seu uso pela IDEO, uma empresa de design norte-
americana. A IDEO ajudou a projetar, por exemplo, os primeiros mouses da Apple.
Outro motivo para essa popularidade deve-se ao fato de Design Thinking ser considerado uma
abordagem capaz de propor
soluções inovadoras
para problemas de maior complexidade.
Por isso, se conhecemos tanto o problema como sua solução, não precisamos usar Design
Thinking.
Exemplos
Apresentamos a seguir um exemplo de problema no qual o uso de Design Thinking não é
interessante e outro problema no qual esse uso seria recomendado.
Exemplo 1:
Suponha que uma empresa seja contratada para construir um sistema Web
para gerenciar a aplicação de provas online com questões de múltipla escolha. Nesse caso, a
solução que está sendo demandada está bastante clara e, logo, não precisamos usar Design
Thinking. A empresa pode, por exemplo, se valer de práticas típicas de métodos ágeis, como
histórias de usuários e conversas com um Product Owner (PO), para identificar e priorizar os
requisitos do sistema.
Exemplo 2:
Suponha que uma universidade está interessada em desenvolver um método
inovador para ensino de Engenharia de Software. Ou seja, nesse caso, não temos clareza sobre
35
CAPÍTULO 8. DESIGN THINKING: PRINCIPAIS CONCEITOS E ATIVIDADES 36
a demanda que está sendo pedida. Não sabemos nem ao certo se esse método demandará a
implementação de sistemas de software. Então, a universidade ou um departamento dela
pode usar Design Thinking para entender melhor o problema e prototipar uma primeira
solução.
8.2 Principais Atividades
Design Thinking não é definido por meio de um conjunto de passos, tarefas e papéis. Isto é,
Design Thinking não é uma fórmula ou um algoritmo, que recebe como entrada um problema
desafiador e gera como saída uma solução inovadora.
Porém, as seguintes atividades principais caracterizam um processo de Design Thinking:
Inspiração
Ideação
Implementação
Nas próximas seções, vamos detalhar essas atividades e também os conceitos e princípios que
são usados ao longo de um processo de Design Thinking.
Antes, no entanto, já gostaríamos de comentar que esses processos são conduzidos por
equipes multidisciplinares
, que incluem profissionais de diversas áreas e com diferentes
experiências. Argumenta-se que multidisciplinaridade é um ingrediente fundamental para
fomentar pensamentos e ideias fora da caixa e, portanto, maximizar as chances de se chegar
em soluções inovadoras.
Inspiração
Design Thinking recomenda que, antes de propor soluções para qualquer problema, os designers
devem entender e vivenciar esse problema, sob o ponto de vista daqueles que estão sofrendo
com ele e que depois irão se beneficiar da solução proposta. Isso é importante para ter ideias
interessantes de soluções.
Para entender o problema, o designer deve entrevistar os usuários, visitar seus locais de
trabalho, observar e estudar seus comportamentos, etc. Como vimos no Capítulo 3
1
, tais
estudos que observam ao vivo os usuários afetados por um problema são chamados de
estudos
etnográficos.
No limite, o designer pode até se passar por um usuário. Por exemplo, em seu livro sobre
Design Thinking, Tim Brown que por um tempo foi um dos principais executivos da IDEO
reporta o caso de um designer que foi encarregado de melhorar o serviço de atendimento
de um hospital. Para isso, ele simulou que tinha quebrado o pé e, então, experimentou na
própria pele o atendimento desse hospital.
Por outro lado, Design Thinking menos ênfase a instrumentos como questionários e
pesquisas de mercado. A seguinte frase de Steve Jobs ilustra os limites desses instrumentos:
Nosso trabalho é descobrir antes o que os clientes querem. Acho que
Henry Ford disse uma vez: se eu perguntasse aos meus clientes o
que eles queriam, eles teriam dito que era um “cavalo mais rápido”.
As pessoas não sabem o que querem até que vo mostre a elas.
1
../cap3.html#engenharia-de-requisitos
CAPÍTULO 8. DESIGN THINKING: PRINCIPAIS CONCEITOS E ATIVIDADES 37
Design Thinking recomenda também prestar atenção a
usuário extremos
, tais como crianças,
idosos, adotantes iniciais de tecnologias, melhores e piores alunos de uma disciplina, pacientes
leves e graves, etc.
Uma outra técnica importante é chamada
reframing
, isto é, tentar reformular o problema
originalmente proposto pelo cliente. Em outras palavras, ver esse problema por um ângulo
ou com um olhar diferente. E, consequentemente, propor uma solução distinta daquela que
era esperada. Por exemplo, suponha uma editora que esteja tendo dificuldades para vender
seus livros. Talvez, em vez de tentar novas estratégias para vender livros, ela possa começar a
oferecer também cursos online baseados nos livros de seu catálogo.
Ideação
Após entender o problema, pode-se avançar para atividades de
geração e testes de ideias
para solucioná-lo. Tais atividades são chamadas de
ideação
. Mais precisamente, incentivam-se
dois tipos de atividades:
Pensamento divergente e convergente
Prototipação e Testes
Pensamento divergente e convergente
Essa primeira atividade é ilustrada na próxima figura:
Figura 8.1: Pensamento divergente e convergente
Em um primeiro momento, todos os membros da equipe são incentivados a rascunhar soluções
para o problema proposto, sejam elas convencionais ou não. Ou seja, em um processo de
brainstorming, soluções “fora da caixa” são incentivadas.
Lembre-se que o objetivo de Design Thinking é gerar soluções inovadoras e não incrementar
marginalmente a qualidade de um produto ou serviço existente. A seguinte frase, do cientista
Linus Pauling — ganhador de dois prêmios Nobel, em Química e da Paz — ilustra a
importância desse primeiro momento:
CAPÍTULO 8. DESIGN THINKING: PRINCIPAIS CONCEITOS E ATIVIDADES 38
Para ter uma boa ideia, vo precisa antes ter muitas ideias.
Continuando com o processo, em um segundo momento, deve-se convergir para um número
limitado de soluções. Ou seja, a equipe deve descartar algumas das soluções propostas
inicialmente. O próprio proponente de uma solução pode desistir da sua proposta ou então
pode-se escolher, por meio de uma votação, as soluções que continuarão no processo.
Prototipação e Testes
Em seguida, ainda como uma atividade de ideação, deve-se construir
protótipos
para as
melhores ideias e testá-los com usuários reais. Esses protótipos podem ser simples e de baixa
fidelidade. Por exemplo, o primeiro protótipo de mouse que a IDEO criou para a Apple era
formado pela esfera de um desodorante roll-on colada em uma embalagem de margarina.
Porém, precisamos reconhecer também que a criação de protótipos é mais difícil em certos
tipos de problemas. Por exemplo, na área de serviços pode ser necessário criar espaços físicos,
mesmo que de forma improvisada e, por exemplo, com veis de isopor. Uma alternativa a
protótipos físicos consiste no desenho de
storyboards
(ilustrações) descrevendo a jornada
de um usuário ao usar um determinado serviço. No caso de software, storyboards podem ser
usados para prototipar a interface do sistema.
Implementação
Por fim, escolhida uma solução, o designer deve acompanhar a sua implementação. Por
exemplo, ele deve ajudar a explicar a solução para o resto da organização. Deve também
ajudar na criação de uma estratégia de divulgação e marketing.
8.3 Conclusão
Talvez, a explicação anterior leve o leitor a pensar que Design Thinking é um processo
sequencial. No entanto, em qualquer atividade pode-se “retroceder” para um ponto anterior.
Por exemplo, durante as atividades de ideação podemos perceber que são necessários mais
dados e insights sobre os problemas dos usuários. Como um outro exemplo, após uma
prototipação, podemos constatar que a ideia que foi escolhida não está funcionando bem.
Logo, devemos escolher ou mesmo gerar uma nova ideia.
Por isso, costuma-se ilustrar essas atividades por meio de “anéis” (ou espaços) interconectados,
como na seguinte figura (extraída do site
2
da IDEO):
Para concluir gostaríamos de listar as habilidades importantes em um Design Thinker, segundo
Tim Brown: “capacidade de identificar padrões na desordem de informações complexas, de
sintetizar novas ideias a partir de fragmentos e de sentir empatia por pessoas diferentes dele
mesmo.
Para Saber Mais
Existe um vasto material sobre Design Thinking. Uma das boas referência sobre o assunto é
o livro
3
de Tim Brown, que usamos como referência para elaboração deste artigo. O seguinte
artigo
4
, do mesmo autor, na Harvard Business Review, também é bastante interessante.
2
https://designthinking.ideo.com/
3
https://www.altabooks.com.br/produto/design-thinking/
4
https://hbr.org/2008/06/design-thinking
CAPÍTULO 8. DESIGN THINKING: PRINCIPAIS CONCEITOS E ATIVIDADES 39
Figura 8.2: Design Thinking não é um processo sequencial
Inclusive, foi dele que tiramos a definição das atividades principais que devem fazer parte
de processos de Design Thinking (inspiração, ideação e implementação). É importante, no
entanto, ressaltar que outros autores costumam incluir mais atividades em suas definições
de Design Thinking, tais como: (re-)definição do problema, síntese, ideação, prototipação e
testes.
Pode ser interessante estudar também conceitos de
Jobs to be Done
, os quais são o tema
do nosso próximo artigo
5
.
Exercícios
1. Marque a alternativa INCORRETA:
a.
Design Thinking é um abordagem iterativa e colaborativa para solução de problemas
complexos e para os quais não existe uma solução conhecida.
b.
Design Thinking propõe que a resolução de um problema ocorra de forma estritamente
sequencial, por meio de três atividades: inspiração, ideação e implementação.
c.
Design Thinking defende o uso de equipes multidisciplinares para solução de problemas
complexos.
d.
Experimentação, por meio da construção e teste de protótipos, é uma das práticas
defendidas por Design Thinking.
e.
Para resolver problemas, Design Thinking recomenda que o time trabalhe de forma
exploratória (divergente) e depois de forma convergente.
2. Qual dessas práticas NÃO é comum quando se usa Design Thinking: a. Reframing b.
Pensamento convergente e divergente c. Prototipação d. Pesquisas de mercado e. Estudos
etnográficos
3. Suponha um time de desenvolvimento de software que usa Scrum no seu dia a dia. Como
e quando os conceitos de Design Thinking podem ajudar esse time? Isto é, em qual situação
hipotética?
4. Descreva um problema que possa se beneficiar do uso de Design Thinking.
5. Qual a diferença entre Design Thinking e Design Sprint? Uma descrição resumida de
Design Sprint é apresentada na Seção 3.5.3 do Capítulo 3
6
do livro.
5
./jobs-to-be-done.html
6
../cap3.html#construindo-o-primeiro-mvp
CAPÍTULO 8. DESIGN THINKING: PRINCIPAIS CONCEITOS E ATIVIDADES 40
Capítulo 9
Jobs to be Done (JTBD) Aplicado a
Produtos de Software
9.1 Introdução
Jobs to be done (JTBD) é uma teoria desenvolvida por Clayton Christensen e colegas, em
2005, para explicar porque clientes escolhem comprar determinados produtos ou serviços. A
ideia é que as pessoas, muitas vezes, não consideram todas as funcionalidades de um produto
ao tomar uma decisão de compra. Em vez disso, elas têm um “trabalho que precisa ser feito”
e para isso elas “contratam” o melhor produto capaz de ajudá-las a realizar esse trabalho
com sucesso.
Portanto, segundo JTBD, as perguntas centrais que devemos responder ao projetar e desen-
volver qualquer produto, seja ele digital ou não, são as seguintes:
Por que os clientes “contratam” nossos produtos? Em quais “circunstâncias”?
Quais os “problemas” de sua vida eles querem resolver com nossos produtos?
Qual o “progresso” eles querem alcançar ao comprar nossos produtos?
JTBD é então uma teoria que foca nas origens das decisões de compra, isto é, nos mecanismos
que explicam porque uma pessoa decide introduzir um produto ou serviço na sua vida. Por
isso, os autores da teoria sempre ressaltam que JTBD procura entender o
mecanismo causal
subjacente às decisões de adoção de produtos, serviços ou inovações.
Exemplo: Milk-shakes Matinais
O exemplo mais comum para explicar JTBD refere-se a consumidores de milk-shake de uma
grande rede de fast-food nos EUA. Ao prestar uma consultoria para a empresa, os autores
da teoria descobriram que um grande número de vendas de milk-shake ocorria nas primeiras
horas da manhã.
Após algumas entrevistas, eles concluíram que os clientes “contratavam” esses milk-shakes
para resolver um “trabalho” específico: para chegar ao seu emprego, eles tinham que dirigir
por um longo tempo e então eles usavam um milk-shake para tornar a viagem menos tediosa.
Como o exemplo tenta ilustrar, nem sempre é trivial para as empresas descobrir os “trabalhos”
para os quais seus “produtos” são contratados. Particularmente, essa “contratação” pode
acontecer em circunstâncias específicas. E também os “trabalhos” podem evoluir com o tempo
e “trabalhos” inesperados podem aparecer.
41
CAPÍTULO 9. JOBS TO BE DONE (JTBD) APLICADO A PRODUTOS DE SOFTWARE42
Por que é importante conhecer os “trabalhos” que motivam a “con-
tratação” de nossos produtos?
Porque assim você pode se preparar para atender seus clientes melhor. Por exemplo, no caso
da rede de fast-food, eles poderiam abrir mais cedo as vendas no drive-thru, evitando que os
clientes tivessem que estacionar o carro e entrar na loja. Uma outra alternativa seria criar um
tipo especial de milk-shake para ser consumido durante viagens matutinas e, por exemplo,
com menos açúcar e mais viscosidade.
Ao pensar com as lentes de JTBD, nós também entendemos melhor quais são os concorrentes
de nossos produtos. Por exemplo, os concorrentes de um software podem incluir não apenas
sistemas semelhantes de outras empresas, mas também uma simples planilha eletrônica ou
mesmo um caderno de anotações.
9.2 O que JTBD tem a ver com software?
A resposta é que tem tudo a ver, pois software é cada vez mais um produto. Quando um
software é visto dessa maneira, o desenvolvimento nunca termina, no sentido de que o sistema
está em constante evolução e sempre precisa incorporar novas funcionalidades para preservar
e ganhar mercados e clientes. Na verdade, essa visão de software-produto é cada vez mais
comum, principalmente em empresas digitais, cujo negócio é concretizado em um ou vários
aplicativos.
Por isso, ao desenvolver e evoluir um produto de software é importante ter em mente os
“trabalhos” para os quais ele está sendo “contratado”. Caso contrário, corremos o risco de ter
um sistema lotado de funcionalidades, mas que não ajuda nos “trabalhos” que seus clientes
precisam entregar.
O tweet abaixo, do fundador do Gumroad, um site para comercialização de produtos digitais,
ilustra muito bem a importância de JTBD no contexto de software.
People don’t want to use your software.They want to lose weight, laugh, be entertained, get
smarter, spend time with loved ones, go home on time, sleep adequately, eat good food, be
happy.Your product is only as good as the experiences it enables people to have.
Sahil Lavingia (@shl) August 15, 2019
O tweet deixa claro que os usuários não usam um software apenas por usar. Na verdade, o
que eles querem é uma melhoria de vida, a qual pode incluir, de forma mais específica, perder
peso, dar algumas risadas, ter momentos de divertimento, ficar mais inteligente, etc.
Exemplo: QuickBooks
No livro no qual apresentam a teoria de JTBD, Christensen e colegas mencionam o caso do
QuickBooks, um software de contabilidade para pequenas empresas vendido pela Intuit.
Essa empresa tinha um sistema de controle de finanças pessoais muito popular nos EUA,
chamado Quicken. A Intuit então percebeu que alguns dos usuários do Quicken também
usavam o sistema, de forma improvisada, para fazer a contabilidade de suas pequenas empresas.
Ou seja, eles preferiam usar o Quicken do que os sistemas de contabilidade que existiam no
mercado, os quais eram complexos e voltados para contadores profissionais.
CAPÍTULO 9. JOBS TO BE DONE (JTBD) APLICADO A PRODUTOS DE SOFTWARE43
A Intuit teve então a ideia de lançar o seu próprio sistema de contabilidade, chamado
QuickBooks, destinado a pequenas empresas e que obteve um grande sucesso. Inclusive, seu
preço era superior ao de vários sistemas concorrentes com mais funcionalidades.
No livro, Christensen e colegas resumem assim esse estudo de caso:
Os concorrentes estavam preocupados em produzir o melhor soft-
ware contábil possível. Por outro lado, Cook [CEO da Intuit] e
sua equipe se concentraram no trabalho que os clientes estavam
tentando realizar [. . . ] E, quando os clientes encontram o produto
correto para atender ao trabalho que eles têm em mãos, normal-
mente estão dispostos a pagar mais.
Exemplo: Twitter
Recentemente, o CEO do Twitter declarou que eles pretendiam focar a evolução da rede
social em três “trabalhos” estratégicos para seus usuários: descobrir o que está acontecendo,
conversar online e receber compensações financeiras pelos seus tweets. O interessante é que
ele não usou o termo requisitos na sua declaração, mas sim “trabalhos”. Para mais detalhes,
veja o seguinte artigo
1
do site da revista Harvard Business Review.
9.3 Descobrindo os “Trabalhos”
Para descobrir os trabalhos para os quais os clientes contratam seu produto de software, é
preciso observá-los e conversar com eles. No exemplo do milk-shake, os autores contam que
fizeram exatamente isso: foram cedo para os restaurantes e perguntaram para os clientes
porque eles estavam “contratando” aqueles milk-shakes logo nas primeiras horas da manhã.
Então, quando estiver definindo histórias de usuário, é importante esclarecer com os clientes
porque eles estão pedindo certas histórias, isto é, qual a motivação e o contexto delas.
Para deixar a motivação de histórias de usuário mais claras, existe o conceito de
job stories
,
que é um template alternativo para escrita de histórias com o seguinte formato:
Quando [situação], eu gosto de [motivo] de tal forma que [benefício]
Veja um exemplo: quando eu dirijo meu carro para o trabalho, eu gosto de tomar um milk-
shake de tal forma que a viagem fique mais agradável e eu não fique com fome logo ao começar
a trabalhar.
No entanto, “histórias de trabalhos” devem ser usadas apenas para histórias menos usuais.
Ou seja, para muitas histórias, a motivação é bem clara e, portanto, o template não agrega
muito valor.
Outra técnica importante é perguntar ao cliente como ele resolve o problema atualmente, isto
é, quando o sistema ainda não inclui a história que ele está pedindo. Isso ajuda a entender o
contexto e a motivação do seu pedido.
1
https://hbr.org/2021/10/how-twitter-applied-the-jobs-to-be-done-approach-to-strategy
CAPÍTULO 9. JOBS TO BE DONE (JTBD) APLICADO A PRODUTOS DE SOFTWARE44
9.4 Conclusão
Quando projetamos um produto, incluindo um software, devemos pensar na oferta, represen-
tada pelas funcionalidades do produto e na demanda, representada pelas reais necessidades
dos clientes. Muitas vezes, começamos pela oferta para então conectá-la à demanda. Por
outro lado, JTBD advoga que o caminho inverso da demanda para a oferta tem mais
chances de dar certo, conforme ilustrado na seguinte figura.
Figura 9.1: JTBD começa entendendo a demanda
No caso específico de software, JTBD destaca-se junto com outras teorias, como Design
Thinking
2
em sistemas cujos requisitos são emergentes, ou seja, não estão claros na cabeça de
nenhum cliente em particular. Nesses casos, vale a pena iniciar entendendo as reais demandas
dos clientes (isto é, os “trabalhos” que são mais importantes para eles), para então definir
“o que” de fato será implementado.
Portanto, e em última instância, teorias como JTBD nos ajudam a evitar a implementação
de sistemas com diversas funcionalidades que geram pouco “progresso” real para os clientes e,
por isso, acabam não sendo usadas.
Para saber mais
Talvez, o primeiro material para aprofundar no estudo de JTBD seja o livro dos autores da
teoria, cujo título em português é “Muito Além da Sorte: Processos Inovadores para Entender
o que os Clientes Querem”, publicado pela Bookman em 2017. Um artigo
3
, publicado na
Harvard Business Review, em setembro de 2016, oferece uma introdução mais resumida a
JTBD.
Exercícios
1. Frequentemente, afirma-se que software pode ser desenvolvido como um projeto ou como
um produto. Primeiro, para entender melhor essas duas formas de desenvolvimento, leia
a seguinte pergunta
4
do FAQ sobre o Capítulo 2 do livro. Em seguida, responda em qual
2
./design-thinking.html
3
https://hbr.org/2016/09/know-your-customers-jobs-to-be-done
4
./processos-faq.html#qual-a-diferen%C3%A7a-entre-times-orientados-por-projeto-e-por-produto
CAPÍTULO 9. JOBS TO BE DONE (JTBD) APLICADO A PRODUTOS DE SOFTWARE45
tipo de cenário JTBD melhor se aplica: software desenvolvido como um projeto ou software
desenvolvido como um produto? Justifique sua resposta.
2. Qual a vantagem de JTBD em relação ao uso de personas? Para conhecer mais sobre
personas, leia a pergunta
5
sobre o tema no FAQ do Capítulo 3 do livro.
3. Em um outro artigo didático, tratamos de Design Thinking (DT)
6
. Descreva (a) uma
característica comum entre JTBD e DT; (b) o principal conceito novo que JTBD introduz
quando comparado a DT.
5
./requisitos-faq.html#o-que-s%C3%A3o-personas
6
./design-thinking.html
Capítulo 10
Mais Alguns Princípios de Projeto
10.1 Introdução
Neste artigo, vamos complementar os princípios de projeto, estudados no Capítulo 5
1
, com
quatro novos princípios:
CQS: Command-Query Separation
DRY: Don’t Repeat Yourself
YAGNI: You Aren’t Gonna Need It
KISS: Keep It Simple, Stupid
Para finalizar, vamos falar sobre ortogonalidade, que é uma propriedade importante em
projetos de projeto
10.2 CQS: Command-Query Separation
Este princípio foi proposto por Bertrand Meyer, o mesmo autor do princípio Aberto/Fechado
(Open/Closed), que origem à letra “O” dos princípios SOLID.
O Princípio da Separação entre Comandos e Queries (ou então
Command-Query Separation, CQS) advoga que devem existir duas categorias de métodos em
uma classe:
Comandos, que são métodos que não retornam valor (isto é, retornam
void
), mas que
possuem efeito colateral. Ou seja, eles alteram o estado do sistema. Por exemplo, eles
gravam alguma informação no banco de dados, alteram um atributo da classe ou uma
variável global, etc.
Queries, que são métodos que apenas retornam valores, sem mudar o estado do sistema.
Então, o princípio recomenda que um método: (1) ou deve ser um comando; (2) ou deve ser
uma query. O que não é recomendável é que um método desempenhe ambos os papéis.
Ou seja, para ficar ainda mais claro: (1) um comando não deve retornar valores; (2) uma
query não deve ter efeitos colaterais.
Bertrand Meyer justifica assim o seu princípio:
1
../cap5.html
46
CAPÍTULO 10. MAIS ALGUNS PRINCÍPIOS DE PROJETO 47
O ato de fazer uma pergunta não deve mudar a sua resposta.
Nessa frase, “fazer uma pergunta” significa chamar uma query. Isso não deve influenciar a
resposta dessa query (caso a gente faça a pergunta de novo, por exemplo) ou a resposta de
uma outra query (que, por exemplo, a gente faça logo a seguir).
O objetivo do princípio é fomentar uma divisão clara entre essas duas categorias de métodos,
pois é mais seguro e simples você chamar uma query do que chamar um comando. Por
exemplo, ao chamar uma query temos certeza de que não vamos alterar nada no sistema. O
mesmo, no entanto, não acontece com comandos.
Em outras palavras, e tentando dar uma interpretação mais ampla para o princípio, ao
projetar um sistema devemos nos esforçar para separar a parte imperativa (comandos) da
parte funcional (queries). E, melhor ainda, devemos procurar maximizar a parte funcional e
minimizar o número de comandos. Isso é importante porque queries (funções) são mais fáceis
de entender, de chamar e de testar.
10.3 DRY: Don’t Repeat Yourself
Este princípio de projeto foi enunciado pela primeira vez no livro The Pragmatic Programmer,
de David Thomas e Andrew Hunt, de 1999. Ele recomenda o seguinte:
Qualquer tipo de conhecimento deve ter uma representação única,
inequívoca e oficial em um sistema.
A forma mais fácil de entender DRY é pensando em termos de digo. Por exemplo, em um
sistema nunca devemos ter duas funções X e Y duplicadas, isto é, com exatamente o mesmo
propósito. Isso é ruim por dois motivos principais:
Suponha que X tenha sido implementada primeiro; logo, o esforço para implementar Y
foi desnecessário, pois existia uma função idêntica no sistema.
Suponha que temos que realizar uma manutenção em X; logo, teremos que repetir esse
esforço em Y.
Porém, DRY não vale apenas para digo, mas para qualquer conhecimento. Por exemplo, às
vezes temos um comentário (ou outra forma de documentação) que apenas repete o que está
no digo. Nesse caso, talvez a refatoração do digo, com nomes e intenções mais claras,
pode torná-lo autoexplicativo. Se isso acontecer, podemos remover o comentário.
Na verdade, na escrita do nosso livro procuramos praticar esse princípio. O livro tem três
versões (HTML, e-book e PDF). Todas elas, no entanto, são geradas a partir de um mesmo
arquivo fonte escrito em Markdown. Se não fosse assim, qualquer pequena correção de
português teria que ser repetida nos três formatos do livro.
Existem outros nomes para DRY. Por exemplo, no livro The Art of Unix Programming, Eric
Raymond usa o termo SPOT (Single Point of Truth).
CAPÍTULO 10. MAIS ALGUNS PRINCÍPIOS DE PROJETO 48
10.4 YAGNI: You Aren’t Gonna Need It
Este princípio é muito comentado entre adotantes de Extreme Programming (XP). Na verdade,
falamos rapidamente sobre ele no Capítulo 2
2
.
Mas, explicando com um pouco mais de calma, a motivação do princípio é o fato de que
desenvolvedores, muitas vezes, projetam e implementam funcionalidades que não serão usadas
ou que não são relevantes no atual momento de vida de um sistema.
Em outras palavras, como é sempre fácil pedir, os clientes de um sistema podem demandar
uma lista enorme de funcionalidades, sendo que algumas delas não são tão importantes.
Inclusive, pode ser que elas nunca sejam fato usadas.
Por isso, o princípio YAGNI (ou “vo não vai precisar disso”) diz o seguinte:
Somente implemente uma funcionalidade quanto tiver fortes evi-
dências de que ela será de fato usada. Ou seja, evite implementar
funcionalidades que serão úteis a médio e longo prazo. Pode ser
que, antes disso, elas perderão importância.
Para dar um exemplo, suponha que vo esteja implementando um aplicativo para entrega de
comida online. E que o cliente que está solicitando o aplicativo sugira que as mensagens
estejam em Português e, também, em Espanhol, pois ele pretende, no futuro, expandir
seu negócio para outros países da América Latina. Nesse contexto, pode ser interessante
argumentar com o cliente se mensagens em espanhol são mesmo importantes, pelo menos
nessa primeira versão do sistema.
10.5 KISS: Keep It Simple, Stupid
Esta sigla é usada em diversos contextos e domínios. Mas, especificamente no caso de projeto
de software, ela diz respeito à tendência de desenvolvedores complicarem o projeto de um
software para além do necessário.
Por exemplo, muitas vezes os desenvolvedores propõem uma arquitetura super complexa,
baseada em padrões elaborados, tais como microsserviços
3
, Arquitetura Hexagonal
4
, Arquite-
tura Limpa
5
, etc. Ou então optam por implementações que fazem um uso exagerado de
padrões de projeto
6
. Não que esses padrões sejam inúteis. . . Mas em algumas aplicações
menores e menos sujeitas a mudanças eles simplesmente não se “pagam”. Ou seja, é como
usar um “canhão para matar uma formiga”.
Por isso, ao projetar um sistema, tente conter sua veia por adotar soluções tecnicamente
sofisticadas. E somente as use quando for necessário.
Quando não seguimos o princípio KISS, costuma-se dizer que solução complexa adotada no
lugar é um exemplo de
overengineering
, ou seja, um uso superestimado e injustificado de
técnicas e princípios de engenharia (no nosso caso, de Engenharia de Software).
2
https://engsoftmoderna.info/cap2.html#pr%C3%A1ticas-de-programa%C3%A7%C3%A3o
3
https://engsoftmoderna.info/cap7.html#microsservi%C3%A7os
4
https://engsoftmoderna.info/artigos/arquitetura-hexagonal.html
5
https://engsoftmoderna.info/artigos/arquitetura-limpa.html
6
https://engsoftmoderna.info/cap6.html
CAPÍTULO 10. MAIS ALGUNS PRINCÍPIOS DE PROJETO 49
Antes de concluir, é interessante correlacionar YAGNI com KISS. O primeiro princípio
recomenda que devemos postergar qualquer implementação até o momento em que ela seja,
de fato, necessária. Quando tal momento chegar, KISS recomenda que a implementação seja
feita da forma mais simples possível.
10.6 Ortogonalidade
Dois componentes de um projeto são ortogonais se mudanças em um deles não afetam o outro.
Logo, eles podem ser livremente combinados ou trocados.
No livro The Pragmatic Programmer, o seguinte exemplo explica essa propriedade de projeto:
Em um sistema bem projetado, o banco de dados é ortogonal à
interface do usuário: pode-se alterar a interface sem afetar o banco
de dados e trocar o banco de dados sem alterar a interface.
Para explicar melhor, suponha um sistema no qual podemos usar um conjunto de interfaces
com o usuário X e um conjunto de bancos de dados Y. Em um sistema ortogonal, a escolha
da interface X não impacta ou restringe a escolha do banco de dados Y. Qualquer combinação
de X e Y é possível. Por isso, podemos imaginar X e Y como duas retas ortogonais, que
formam um plano, no qual qualquer coordenada (x,y) representa uma configuração possível
do sistema.
Em geral, ortogonalidade é uma propriedade que costuma estar presente, de graça, em sistemas
bem projetados, cujos módulos possuem alta coesão e baixo acoplamento, tal como estudamos
no Capítulo 5
7
.
Exercícios
1. Seja uma aplicação CRUD (Create, Read, Update, Delete). Quais dessas operações devem
ser implementadas como Comandos? E quais devem ser Queries?
2. Seja a classe
Stack
implementada no seguinte arquivo
8
. (1) Qual método dessa imple-
mentação viola o princípio de Separação de Comandos e Queries (CQS)? (2) Como você
reimplementaria esse método de forma compatível com CQS? (3) Vo acha que valeria a
pena adotar a solução compatível com CQS nesse caso?
7
../cap5.html#coes%C3%A3o
8
https://gist.github.com/mtov/3601acd0b32a1d0a85b4a81a43af4284
Capítulo 11
dulos devem ser Profundos!
11.1 Introdução
John Ousterhout
1
é professor da Universidade de Stanford e criador da linguagem de script
Tcl/Tk. Em 2018, ele lançou um livro
2
sobre filosofia e princípios de projeto de software.
Neste livro, uma das principais recomendações e, provavelmente, uma das mais originais, se
comparada com o restante da literatura sobre projeto de software é a seguinte:
os dulos
de um sistema devem ser profundos.
Vamos, primeiro, definir o que é um dulo. Basicamente, é qualquer elemento de digo
que tem uma implementação e uma interface. Assim, podemos ter dulos de diferentes
tamanhos, incluindo funções, classes, subsistemas, pacotes, etc.
11.2 dulos Profundos
Mas voltando à recomendação de Ousterhout, um dulo é profundo (deep) quando sua
implementação é bem mais complexa do que sua interface. Em outras palavras, dulos
devem oferecer para seus clientes uma interface simples. Porém, por trás dessa interface, deve
existir uma implementação complexa.
O contrário é um dulo raso, quando a interface é quase tão complexa quanto a própria
implementação do dulo. Para ajudar a entender, vejas as seguintes ilustrações retiradas
do livro:
Como exemplo de módulo profundo, o autor menciona o sistema de arquivos do Unix, cuja
interface é composta por apenas cinco funções:
int open ( const char * path , int flags , mode_t pe rm issions );
ssize_t read ( int fd , void * buffer , size_t count );
ssize_t write ( int fd , const void * buffer , size_t count );
off_t lseek ( int fd , off_t offset , int r efere nce Posit ion );
int close ( int fd );
Ou seja, a implementação de um sistema de arquivos em um sistema operacional é bastante
complexa, pois envolve lidar com uma variedade de dispositivos de hardware, criar estruturas
1
https://web.stanford.edu/~ouster/cgi-bin/home.php
2
https://www.goodreads.com/book/show/39996759-a-philosophy-of-software-design
50
CAPÍTULO 11. MÓDULOS DEVEM SER PROFUNDOS! 51
Figura 11.1: dulos profundos vs dulos rasos
para armazenar arquivos, gerenciar permisssões, permitir acesso concorrente a arquivos,
manipular caches, etc. Porém, toda essa monstruosa complexidade fica escondida por trás de
uma interface muito simples (as cinco funções mostradas acima). Inclusive, o autor comenta
que a implementação de sistemas de arquivos no Unix evoluiu radicalmente ao longo dos
anos, mas de forma invisível aos usuários, pois a assinatura das cinco funções de interface
permaneceu inalterada.
O autor menciona um segundo exemplo de módulo profundo: coletores de lixo. Apesar de
complexos, algoritmos de coleta de lixo são encapsulados em dulos que, basicamente, não
possuem uma interface. Pelo menos uma interface visível à maioria dos programadores de
linguagens como Go ou Java.
Nas minhas aulas, eu costumo usar também uma metáfora alternativa, originalmente sugerida
em um livro
3
do prof. Bertrand Meyer. Segundo ele, dulos devem ser como icebergs, com
uma pequena “ponta” (a interface) e uma grande base (a implementação), que fica submersa
e invisível aos olhos externos. Veja a figura usada no livro de Meyer:
Figura 11.2: dulos devem ser como icebergs
3
https://en.wikipedia.org/wiki/Object-Oriented_Software_Construction
CAPÍTULO 11. MÓDULOS DEVEM SER PROFUNDOS! 52
11.3 dulos Rasos
Por outro lado, a interface de um módulo raso (shallow) é relativamente complexa, quando
comparada com a implementação do dulo. Como exemplo extremo, o autor menciona a
seguinte função:
private void add Nul lVa lue For Att rib ute ( String attribute ) { // interface
data . put ( attribute , null ); // implementa ção
}
Para ele, é mais simples e fácil chamar diretamente:
data . put ( attribute , null );
do que criar uma função com apenas essa finalidade. Ou seja, a função
addNullValueForAttribute
adiciona complexidade ao sistema, mas sem proporcionar grandes benefícios.
Exercícios
1. dois exemplos de dulos profundos. Para facilitar sua resposta, pense em pacotes ou
bibliotecas de sua linguagem preferida.
2. Seja a seguinte afirmação: dulos profundos implicam obrigatoriamente em funções ou
classes grandes, com muitas linhas de digo. Ela é verdadeira ou falsa? Justifique e discuta.
3. Suponha o seguinte método da classe String de Java:
public bo olean isEmpty () {
return value . length == 0;
}
Esse método é raso? Justifique e discuta.
Sugestão: Para responder à pergunta pense no digo que chama
isEmpty()
. Por exemplo,
vo trocaria o seguinte digo cliente
if ( myString . isEmpt y ()) {
...
}
por um digo como o seguinte?
if ( myString . lenght () == 0) {
...
}
4. Seja a seguinte afirmação: todo dulo raso é pequeno, mas nem todo dulo pequeno é
raso. Ela é verdadeira ou falsa? Justifique e discuta. (observação: um dulo pequeno é
aquele que tem poucas linhas de digo.)
5. Seja a seguinte afirmação retirada do livro de Refactoring de Martin Fowler:
CAPÍTULO 11. MÓDULOS DEVEM SER PROFUNDOS! 53
Small methods really work only when you have good names, so
you need to pay attention to naming. People sometimes ask me
what length I look for in a method. To me length is not the issue.
The key is the semantic distance between the method name and
the method body.
Explique conceito de distância semântica entre o nome de um método e o corpo desse método,
mencionado na citação acima.
Capítulo 12
Domain-Driven Design (DDD): Um
Resumo
12.1 Introdução
Domain-Driven Design (DDD) é um conjunto de princípios para projeto de software, organi-
zados e sistematizados em 2003, por Eric Evans, em um livro com o mesmo nome.
Os princípios defendidos por DDD têm, no seu conjunto, um objetivo central: permitir o
desenvolvimento de sistemas cujo design é centrado em conceitos próximos e alinhados com
um domínio de negócio.
O
domínio
de um sistema consiste da área e problema de negócio que ele pretende resolver.
Neste artigo, para explicar DDD, vamos usar como exemplo um sistema para gerenciar uma
biblioteca. Logo, esse problema gerenciamento de bibliotecas constitui o domínio do nosso
sistema de exemplo.
DDD defende que os
desenvolvedores
devem ter um profundo conhecimento do domínio do
sistema que eles desenvolvem. Esse conhecimento deve ser obtido por meio de conversas e
discussões frequentes com
especialistas no domínio
(ou no negócio). Portanto, o design do
sistema deve ser norteado para atender ao seu domínio. E não, por exemplo, para se moldar
a uma determinada tecnologia de programação. Em suma, o design é dirigido pelo domínio, e
não por frameworks, arquiteturas, linguagens de programação, etc.
DDD defende que a separação entre domínio e tecnologia deve ser promovida e expressa
na arquitetura do sistema. Para tanto, padrões como Arquitetura em Camadas (estudado
no Capítulo 7
1
), Arquitetura Limpa (tratada neste outro artigo didático
2
) ou Arquitetura
Hexagonal (também coberta em um artigo
3
separado) podem ser usados.
Antes de avançarmos, é importante mencionar também que DDD se sobressai quando é usado
em sistemas para domínios complexos, cujas regras de negócio são mais difíceis de serem
imediatamente entendidas e implementadas pelos desenvolvedores.
1
../cap7.html#arquitetura-em-camadas
2
./arquitetura-limpa.html
3
./arquitetura-hexagonal.html
54
CAPÍTULO 12. DOMAIN-DRIVEN DESIGN (DDD): UM RESUMO 55
12.2 Linguagem Ubíqua
Linguagem Ubíqua
(ou
Linguagem Onipresente
) é um conceito central de DDD. Ela
consiste de um conjunto de termos que devem ser plenamente entendidos tanto por especialistas
no domínio (usuários do sistema) como por desenvolvedores (implementadores do sistema).
Para um projeto de software dar certo, DDD defende que esses dois papéis especialistas
no domínio e desenvolvedores devem falar a mesma língua, que vai constituir a chamada
Linguagem Ubíqua do sistema. Essa ideia é ilustrada na seguinte figura:
Figura 12.1: A linguagem ubíqua representa o conhecimento compartilhado entre especialistas
do negócio e desenvolvedores.
A figura deixa claro que existem termos que os especialistas de domínio conhecem.
outros termos, de cunho tecnológico, são do conhecimento apenas dos desenvolvedores. Porém,
existe um conjunto de termos que devem ser do conhecimento de ambos, os quais formam a
Linguagem Ubíqua do sistema.
Os termos da Linguagem Ubíqua são usados com dois propósitos:
Para possibilitar uma comunicação fluida entre desenvolvedores e especialistas no
domínio.
Para nomear entidades do digo do sistema, como classes, métodos, atributos, pacotes,
dulos, tabelas de bancos de dados, rotas de APIs, etc.
Além de clarificar o significado dos termos da linguagem ubíqua, é importante que se definam
os relacionamentos e associações que existem entre eles.
Exemplo
: no nosso sistema de bibliotecas, a Linguagem Ubíqua inclui termos como os
seguintes:
Livro, Exemplar, ISBN, Bibliotecária, Usuário, Acervo, Reserva,
Empréstimo, Multa, Catálogo
CAPÍTULO 12. DOMAIN-DRIVEN DESIGN (DDD): UM RESUMO 56
Por outro lado, alguns termos são de domínio apenas dos desenvolvedores, tais como proxy,
observadores, cache, camadas, rotas, dentre outros. Existem ainda termos que são do
conhecimento apenas de bibliotecárias, como certos formatos para definição de ISBNs, que
não são usados no Brasil.
Devemos definir também os relacionamentos e associações entre esses termos, como exemplifi-
cado a seguir:
Um Livro pode ter um ou mais Exemplares.
Uma Reserva pode ser feita para no máximo três Livros.
Existem três tipos de Usuário: Aluno, Professor e UsuárioExterno.
O Acervo da biblioteca é formado por um conjunto de Livros.
Para documentar de forma visual esses relacionamentos pode ser usado um
Diagrama de
Classes de UML, conforme estudamos no Capítulo 4
4
.
12.3 Objetos de Domínio
DDD foi proposto pensando em sistemas implementados em linguagens orientadas a objetos.
Então, quando se define o design desses sistemas, alguns tipos importantes de objetos se
destacam. Dentre eles, DDD lista os seguintes:
Entidades
Objetos de Valor
Serviços
Agregados
Repositórios
Esses tipos de objetos de domínio devem ser entendidos como as “ferramentas conceituais”
que um projetista deve lançar mão para projetar com sucesso um determinado sistema. Por
isso, eles são chamados também dos
building blocks
de DDD. Iremos comentar sobre cada
um deles a seguir.
Entidades e Objetos de Valor
Uma
entidade
é um objeto que possui uma identidade única, que o distingue dos demais
objetos da mesma classe. Por exemplo, cada
Usuário
da nossa biblioteca é uma entidade, cujo
identificador é o seu número de matrícula na universidade.
Por outro lado,
objetos de valor
(value objects) não possuem um identificador único. Assim,
eles são caracterizados apenas por seu estado, isto é, pelos valores de seus atributos. Por
exemplo, o
Endereço
de um
Usuário
da biblioteca é um objeto de valor. Veja que se dois
Endereços
tiverem exatamente os mesmos valores para
rua
,
número
,
cidade
,
CEP
, etc, eles serão
idênticos.
Outros exemplos de objetos de valor incluem: Moeda, Data, Fone, Email, Hora, Cor, etc.
Por que distinguir entre entidades e objetos de valor?
Entidades são objetos mais
importantes e devemos, por exemplo, projetar com cuidado como eles serão persistidos e
depois recuperados de um banco de dados. Devemos também tomar cuidado com o ciclo de
vida de entidades. Por exemplo, podem existir regras que governam a criação e remoção de
entidades. No caso da nossa bibliotecas, não se pode remover um
Usuário
se ele tiver um
Empréstimo pendente.
4
../cap4.html#diagramas-de-classes
CAPÍTULO 12. DOMAIN-DRIVEN DESIGN (DDD): UM RESUMO 57
objetos de valor são mais simples. E também eles devem ser
imutáveis
, ou seja, uma
vez criados, não deve ser possível alterar seus valores internos. Por exemplo, para alterar o
Endereço
de um
Usuário
devemos abandonar o objeto antigo e criar um objeto com o
Endereço
novo. Os benefícios de objetos imutáveis foram discutidos no Capítulo 9
5
.
É interessante mencionar também que, recentemente, algumas linguagens de programação
passaram a oferecer suporte sintático para implementação de objetos de valor. Por exemplo,
nas versões mais novas de Java, eles podem ser implementados por meio de records
6
.
Serviços
Existem operações importantes do domínio que não se encaixam em entidades e objetos de
valor. Assim, o ideal é criar objetos específicos para implementar essas operações. No jargão
de DDD, esses objetos são chamados de
serviços
. Em alguns sistemas, é comum ver esses
objetos sendo chamados também de gerenciadores ou controladores.
A assinatura das operações de um objeto de serviço pode incluir entidades e objetos de valor.
No entanto, objetos de serviço não devem possuir estado, isto é, eles devem ser
stateless
.
Por isso, eles não costumam ter atributos, mas apenas métodos.
Serviços normalmente são implementados como
singletons
, ou seja, possuem uma única
instância durante a execução do sistema. Mais detalhes sobre esse padrão de projeto no
Capítulo 6
7
.
Exemplo
: no nosso sistema de bibliotecas, podemos ter um serviço que implementa as
seguintes operações:
class Se rvicoDeE mpr est imo {
...
void emprest arLiv ro ( Usuario , Livro ) {...}
void de volve rLivro ( Usuario , Livro ) {...}
...
}
Na primeira operação, realiza-se o empréstimo de um
Livro
para um certo
Usuário
. Na segunda
operação, um Usuário devolve um Livro que ele tenha sob empréstimo.
Ambas as operações não são específicas nem de
Usuário
, nem de
Livro
. Logo, a recomendação
de criar um objeto de serviço para acomodá-las.
Agregados
Agregados
(aggregates) são coleções de entidades e objetos de valor. Ou seja, algumas vezes
não faz sentido raciocinar sobre entidades e objetos de valor de forma individual. Em vez
disso, temos que pensar em grupos de objetos para ter uma visão consistente com o domínio
que estamos modelando.
Um agregado possui um objeto raiz, que deve ser uma entidade. Externamente, o agregado é
acessado a partir dessa raiz. A raiz, por sua vez, referencia os objetos internos do agregado.
Porém, esses objetos internos não devem ser visíveis para o resto do sistema, ou seja, apenas
a raiz pode referenciá-los.
5
../cap9.html#objetos-mut%C3%A1veis
6
https://docs.oracle.com/en/java/javase/16/language/records.html
7
../cap6.html#singleton
CAPÍTULO 12. DOMAIN-DRIVEN DESIGN (DDD): UM RESUMO 58
Como formam uma unidade coerente, agregados são persistidos em conjunto em bancos de
dados. A deleção de um agregado, da memória principal ou de um banco de dados, implica
na deleção da sua raiz e de todos os objetos internos.
Como eles são objetos mais complexos e com objetos internos, pode ser interessante implemen-
tar métodos especificamente para criação de agregados, os quais são chamados de
fábricas
.
Ou seja, tais métodos são implementações do padrão de projeto de mesmo nome.
Exemplo
: No sistema de bibliotecas, um
Empréstimo
possui um
Usuário
(que é uma entidade),
uma data de realização (que é um objeto de valor) e uma lista de
Itens Emprestados
. Cada
Item Emprestado
contém informações sobre um certo
Livro
que foi emprestado e sua data de
devolução (estamos pressupondo que alguns livros devem ser devolvidos mais rapidamente do
que outros, por exemplo).
Logo,
Empréstimo
e
Itens de Empréstimo
formam um agregado. Isto é, uma entidade única do
ponto de vista conceitual.
Empréstimo
é a raiz do agregado e
Itens Emprestados
são os seus
objetos internos, que não podem ser manipulados sem passar antes pela raiz.
Observe que
Itens Emprestados
referenciam
Livros
, porém esses últimos não fazem parte do
agregado, pois eles têm vida própria, isto é, eles existem independentemente de estarem
emprestados ou não.
Repositórios
Para implementar certos serviços do domínio precisamos antes obter referências para determi-
nados objetos.
Por exemplo, suponha um serviço que lista os
Empréstimos
realizados por um
Usuário
. Para
implementá-lo, não podemos assumir que todos os agregados do tipo
Empréstimo
estão na
memória principal. Na verdade, em qualquer sistema real, eles estão armazenados em um
banco de dados.
Um
repositório
é então um objeto usado para recuperar outros objetos de domínio de um
banco de dados. Seu objetivo é prover uma abstração que blinde os desenvolvedores de
preocupações relacionadas com acesso a bancos de dados. Normalmente, repositórios são
criados para recuperar entidades ou agregados.
Em outras palavras, um repositório oferece uma abstração para o banco de dados usado pelo
sistema e, assim, permite que os desenvolvedores continuem focados no domínio, em vez de
ter sua atenção desviada, em certos momentos, para uma tecnologia de armazenamento de
dados. Em termos mais concretos, um repositório permite manipular objetos de domínio
como se eles fossem listas (ou coleções) armazenadas na memória principal. A implementação
interna do repositório cuida então de ler e salvar essas listas no banco de dados.
Exemplo:
No sistema de bibliotecas, existe um repositório com métodos para recuperar
Empréstimos salvos em um banco de dados:
class Re pos ito rio DeE mpr est imo s {
List < Emprestimo > f ind Emp res tim osD eUs uar io ( Usuario u ) {...}
List < Emprestimo > f ind Emp res tim osP orD ata ( Data inicio , Data fim ) {...}
List < Emprestimo > f ind Emp res tim osV enc ido s () {...}
...
}
CAPÍTULO 12. DOMAIN-DRIVEN DESIGN (DDD): UM RESUMO 59
Além dos métodos
find*
, um repositório pode implementar métodos para salvar, atualizar e
remover objetos:
class Re pos ito rio DeE mpr est imo s {
// m é todos find * ( veja acima )
void salvar ( Emprestimo e ) {...}
void atualizar ( E mprestimo e) {...}
void remover ( Emprestimo e) {...}
}
12.4 Contextos Delimitados
Com o tempo, sistemas de software ficam mais complexos e abrangentes. Por isso, é irrealista
imaginar que sistemas de organizações grandes e complexas vão possuir um modelo de domínio
único e baseado na mesma linguagem ubíqua.
Em vez disso, é natural que tais organizações tenham sistemas que atendem a usuários com
perfis e necessidades diferentes, o que complica a definição de uma linguagem ubíqua. A
solução para esse problema consiste em quebrar tais domínios complexos em domínios menores,
os quais em DDD são chamados de Contextos Delimitados (Bounded Contexts).
Exemplo:
Suponha que a nossa biblioteca tenha um setor financeiro. Esse setor tem
necessidades específicas, que começam a justificar um projeto separado, com uma linguagem
própria. Por exemplo, nesse domínio financeiro, a classe
Usuário
pode, inclusive, ser chamada
de Cliente e ter novos atributos.
12.5 Camada Anticorrupção
Às vezes, temos que integrar sistemas que estão em contextos delimitados diferentes. Por
exemplo, um sistema A precisa usar serviços de um sistema B, que pode inclusive ser um
sistema externo, isto é, de uma outra organização. Para evitar que A tenha que se adaptar
e usar, mesmo que parcialmente, a linguagem ubíqua de B, pode-se usar uma
Camada
Anticorrupção para mediar essa comunicação.
Essa camada é formada por três tipos principais de classes:
Classes de Serviço, cujos métodos serão chamados por A e que, portanto, seguem a
linguagem ubíqua desse sistema.
Classes Adaptadoras
8
, que convertem o modelo e os tipos de dados de B para o modelo
e tipos de dados de A. Ou seja, essas classes vão isolar elementos próprios de B e evitar
que eles cheguem até o sistema A.
Uma Classe de Fachada
9
, usada para acessar o sistema B. O papel dessa classe é facilitar
o uso de B, principalmente quando ele é um sistema legado com uma interface complexa
e antiga.
Logo, o fluxo costuma ser o seguinte:
8
../cap6.html#adaptador
9
../cap6.html#fachada
CAPÍTULO 12. DOMAIN-DRIVEN DESIGN (DDD): UM RESUMO 60
Sistema A -> [ Servi ç os -> Adap ta dores -> Fachada ] -> Sistema B
Nesse fluxo, as classes entre colchetes constituem a Camada Anticorrupção que foi construída
para integrar os sistemas A e B.
12.6 Conclusão
Em um material de referência, que escreveu em 2014, Eric Evans define assim DDD:
DDD é uma abordagem para desenvolvimento de sistemas de soft-
ware complexos, em que: (1) o foco está no domínio do sistema;
(2) desenvolvedores e especialistas no negócio devem explorar esse
domínio de forma colaborativa; (3) como resultado, eles devem
se comunicar usando uma linguagem ubíqua, mas dentro de um
contexto delimitado.
A linguagem ubíqua do sistema deve ser usada também no seu digo, para nomear variáveis,
parâmetros, métodos, classes, pacotes, etc. Especificamente, um projeto DDD deve fazer uso
dos seguintes tipos de objetos principais: entidades, objetos de valor, serviços, agregados e
repositórios.
Exercícios
1. Para fixar os principais conceitos de DDD, responda ao seguinte exercício
10
de V ou F,
com correção online.
2. Suponha que vo trabalha em uma empresa que possui um aplicativo para entrega de
comida pela Internet. Vo ficou responsável pelo projeto da camada de domínio do backend
desse sistema. Para isso, vo decidiu usar DDD. Descreva então:
(a) Cinco termos da linguagem ubíqua do sistema
(b) Três entidades
(c) Três objetos de valor
(d) Um agregado (incluindo o objeto raiz e seus objetos internos)
(e) Dois métodos de um serviço
(f) Dois métodos de um repositório
Em suas respostas, basta citar os nomes que foram pedidos.
3. Suponha um sistema de comércio eletrônico, com as seguintes classes
Pedido
,
ItensPedido
e
Produto
. Desenhe um diagrama de classes que represente as relações entre essas classes. Quais
classes constituem um agregado? Qual classe está fora do agregado e porquê?
4. Depois de aprender DDD, um desenvolvedor resolveu estruturar seu sistema, que é bastante
complexo, como mostrado a seguir. Basicamente, ele criou um pacote (ou, se preferir, um
dulo ou diretório) para agrupar os arquivos que implementam os tipos de objetos de
domínio preconizados por DDD. Essa decisão é recomendável ou não? Em outras palavras,
ela é fiel e consistente com os princípios de DDD? Justifique sua resposta.
10
../exercicios/exvf.html#/cap/DDD
CAPÍTULO 12. DOMAIN-DRIVEN DESIGN (DDD): UM RESUMO 61
Raiz
| __ Entidades
| | arquiv os que implementam entidades
|
| __ Agregados
| | arquiv os que implementam agregados
|
| __ Ob jetosVa lo r
| | arquiv os que implementam objetos de valor
|
| __ Servicos
| | arquiv os que implementam servi ç os
|
| __ Re positor io s
| | arquiv os que implementam reposit ó rios
Capítulo 13
Criando Objetos Compostos com o
Padrão Composite
13.1 Introdução
Neste artigo, vamos apresentar o padrão de projeto
Composite
, que por ser um padrão
bem simples não foi tratado no Capítulo 6
1
do livro.
Para isso, vamos usar a mesma estrutura dos padrões de projeto que estudamos no referido
capítulo. Ou seja, iremos apresentar o padrão descrevendo primeiro um contexto, depois um
problema enfrentado nesse contexto e, por fim, a solução que é proposta por Composite.
13.2 Contexto
Suponha que estamos desenvolvendo um editor gráfico, similar ao Paint (para Windows) ou
Canva (Web). Nesse editor, temos interfaces e classes como as seguintes:
interface Figura {
void desenha ();
void mudaCor DeFun do ( Cor cor );
}
class Circulo i mp le ments Figura {
...
}
class T ri angulo implements Figura {
...
}
13.3 Problema
No nosso editor, queremos implementar uma funcionalidade que permita
agrupar figuras
e
então tratar a figura resultante desse agrupamento como uma figura única.
Por exemplo, uma figura agrupada pode ser composta por um círculo e dois triângulos (veja
ilustração). Criado esse agrupamento, queremos, em seguida, realizar uma operação no
1
../cap6.html
62
CAPÍTULO 13. CRIANDO OBJETOS COMPOSTOS COM O PADRÃO COMPOSITE63
mesmo por exemplo, mudar a cor de fundo de suas três figuras usando para isso uma
única chamada de método.
Figura 13.1: Figuras simples e agrupadas
Explicando melhor, um digo cliente vai poder trabalhar com figuras simples ou com figuras
agrupadas da mesma forma, usando apenas a interface
Figura
. Esse digo não vai precisar
saber qual tipo de figura (simples ou agrupada) ele está manipulando.
13.4 Solução
O
padrão de projeto Composite
é a solução para o problema que enunciamos. Ele permite
armazenar objetos compostos (ou agrupados) em uma estrutura com forma de árvore e então
possibilita que clientes manipulem esses objetos como se fossem objetos simples.
A classe principal do padrão é aquela responsável pelo agrupamento. No nosso exemplo,
temos então que criar uma classe como a seguinte:
class Figu raAgr upada implements Figura {
private ArrayList < Figura > figuras = new ArrayList < Figura >();
// lista que vai armazenar as figuras agrupadas
public void adicionar ( Figura fig ) {
figuras . add ( fig );
}
public void remover ( Figura fig ) {
figuras . remove ( fig );
}
public void desenha () {
for ( Figura fig : fi guras ) {
fig . de senha ();
}
}
public void mudaC or DeFun do ( Cor cor ) {
for ( Figura fig : fi guras ) {
fig . mud aCorD eFund o ( cor );
CAPÍTULO 13. CRIANDO OBJETOS COMPOSTOS COM O PADRÃO COMPOSITE64
}
}
}
Dois aspectos principais devem ser ressaltados sobre a classe FiguraAgrupada:
Ela implementa métodos para adicionar e remover uma figura do agrupamento.
Ela também é uma figura, pois implementa a interface
Figura
. A implementação
dos métodos de
Figura
apenas delega a mesma operação para todas as figuras do
agrupamento, usando para isso um comando for.
Veja também um diagrama UML com as classes que criamos até agora:
Figura 13.2: Classes de uma implementação do padrão de projeto Composite
Como resultado, para um digo cliente, não interessa se ele está trabalhando com um figura
simples ou com uma figura agrupada, como no seguinte caso:
class Cliente {
void foo ( Figura fig ) {
...
fig . de senha (); // desenha figura simple s ou agrupada
...
}
}
class Main {
public main () {
Cliente cliente = new Cliente ();
Circulo c1 = new Circulo ();
cliente . foo ( c1 ); // chama foo com figura simples
Triangulo t1 = new Triangulo ();
Triangulo t2 = new Triangulo ();
CAPÍTULO 13. CRIANDO OBJETOS COMPOSTOS COM O PADRÃO COMPOSITE65
Fi guraA grupada grupo = new Fi guraA grupa da ();
grupo . a di ci on ar ( c1 );
grupo . a di ci on ar ( t1 );
grupo . a di ci on ar ( t2 );
cliente . foo ( grupo ); // chama foo com figura agrupada
}
}
Por fim, veja que podemos criar uma hierarquia, em forma de árvore, com figuras agrupadas
“contendo” outras figuras agrupadas, como em:
Fi guraA grupada grupo2 = new Fi guraA gr upada ();
grupo2 . adicionar ( grupo1 ); // figura agrupada conten do outra figura agrupada
13.5 Conclusão
Devemos usar o padrão Composite quando precisamos representar de forma uniforme objetos
simples e objetos compostos (isto é, objetos formados pelo agrupamento de objetos simples).
Isso simplifica a implementação de digo que precisa manipular tais objetos, pois fica
transparente se a manipulação ocorre sobre um objeto simples ou sobre um objeto composto.
digo fonte: O digo fonte do exemplo usado neste artigo está disponível aqui
2
.
Exercícios
1. Existem três tipos de classes ou interfaces no padrão Composite:
Uma interface visível para o cliente (no nosso exemplo, Figura)
Classes de objetos simples (no nosso exemplo, Circulo e Triangulo)
Classe responsável pela composição (no nosso exemplo, FiguraAgrupada)
Considerando essas classes, pense em um outro exemplo de uso do padrão Composite. E
então responda:
(a) Qual é a interface visível para o cliente? Quais métodos ela define?
(b) Quais são as classes de objetos simples? Basta citar o nome delas.
(c) Qual a classe responsável pela composição? Basta citar o nome dela.
2
https://repl.it/@mtvalente/Padrao-de-Projeto-Composite
Capítulo 14
O que é Injeção de Dependência?
14.1 Introdução
Injeção de Dependência não faz parte dos padrões de projeto do livro GoF. Porém, a solução
é frequentemente listada como um padrão que poderia ser incluído em uma possível segunda
edição do livro.
A ideia de Injeção de Dependência é bastante simples e quase que uma aplicação literal do
seu nome. Vamos então explicá-la em quatro passos:
1. Suponha que uma classe A dependa de uma outra classe B:
class A {
B b; // A depende de B
}
2.
No entanto, para seguir a ideia do padrão, a classe
A
não deve instanciar diretamente
isto é, no seu digo objetos do tipo B, como em:
class A {
B b = new B (); // i ns tancia ção in compat í vel com inje ç ã o de depend ê ncia
}
3. Em vez disso, a classe A deve receber essa dependência por meio de um construtor:
class A {
B b;
A(B b ) { // inje çã o de depend ê ncia via construto r
this .b = b;
}
}
ou então receber a dependência por meio de um método set:
class A {
B b;
void setB (B b ) { // inje çã o de depend ê ncia via setter
this .b = b;
66
CAPÍTULO 14. O QUE É INJEÇÃO DE DEPENDÊNCIA? 67
}
}
Logo, agora fica fácil entender o nome do padrão: as dependências de uma classe são
injetadas
nela, seja por meio de chamadas do seu construtor ou por meio de chamadas de um setter.
4.
Na verdade, o mais recomendado é que o digo de
A
use uma interface (isto é, uma
abstração) para a classe concreta
B
. Ou seja, em vez de usar
B
(uma classe concreta),
deve-se usar IB (uma interface):
class A {
IB b ; // depend ê ncia para uma interface IB
A(IB b ) {
this .b = b;
}
}
ou
class A {
IB b ; // depend ê ncia para uma interface IB
void setterB ( IB b) {
this .b = b;
}
}
Ou seja: quando usamos Injeção de Dependência devemos fazer uso do princípio de projeto
Prefira Interfaces a Classes Concretas, que estudamos no Capítulo 5
1
.
Para concluir, as vantagens de Injeção de Dependência são:
Injeção de Dependência torna mais fácil mudar a dependência concreta (
B
) usada por
uma classe
A
. Por exemplo,
A
pode ser uma classe que precisa enviar mails. Para isso,
ela faz uso de uma classe
B1
. Amanhã, no entanto, podemos decidir que os mails serão
enviados por uma classe B2. Para isso, basta que B1 e B2 implementem a interface IB.
Injeção de Dependência torna mais fácil o teste da classe
A
, pois podemos mockar mais
facilmente a dependência para
B
. Por exemplo, em vez de um serviço de mail real (
B1
ou
B2
), podemos usar um serviço de mail fictício, que apenas emule o envio de uma
mensagem simples. Para isso, basta que esse serviço fictício implemente a interface IB.
Se vo ainda não sabe o que é um mock, recomendamos a leitura da seção do Capítulo
8
2
sobre o assunto.
14.2 Frameworks para Injeção de Dependência
Uma desvantagem de Injeção de Dependência é que a responsabilidade por criar as dependên-
cias isto é, instanciar os objetos que serão passados para a classe é transferida para os
seus clientes, como no seguinte exemplo:
class Cliente {
1
../cap5.html#princ%C3%ADpio-de-invers%C3%A3o-de-depend%C3%AAncias
2
../cap8.html#mocks
CAPÍTULO 14. O QUE É INJEÇÃO DE DEPENDÊNCIA? 68
void foo () {
IB b = new B1 (); // insta nc ia depend ê ncia
A a = new A(b); // injeta depend ê ncia na classe A
...
}
}
No entanto, existem frameworks que evitam que os clientes tenham esse trabalho extra.
Basicamente, eles assumem a responsabilidade de criar as dependências e injetá-las nas classe
de destino.
De um modo genérico, costuma funcionar assim:
class A {
IB b ;
@Inject // anota çã o disp onibi lizad a pelo framework
A(IB b ) {
this .b = b;
}
}
A anotação
@Inject
indica que a classe
A
quer fazer uso de Injeção de Dependência. E que,
portanto, as dependências do seu construtor deverão ser criadas e injetadas pelo framework
de injeção de dependências.
No entanto, algum desenvolvedor deverá declarar a classe dos objetos que devem ser criados
toda vez que o framework precisar chamar um construtor anotado com
@Inject
. Isso pode
acontecer em um arquivo XML, um outro tipo de arquivo ou mesmo em um método específico.
Em qualquer caso, a ideia é declarar uma tabela com duas colunas: interface e classe concreta.
No nosso exemplo, essa tabela deve incluir a linha
(IB, B1)
. Essa linha define que objetos do
tipo
B1
deverão ser instanciados e injetados em todas as classes que precisarem de dependências
do tipo IB.
Por fim, o cliente, quando precisar instanciar um objeto de uma classe que usa
@Inject
, não
precisará mais chamar o operador
new
, mas sim um método do próprio framework, como em:
class Cliente {
void foo () {
A a = DIF . get In stance (A . class );
...
}
}
Ou seja,
getInstance
é um método que instancia classes que usam Injeção de Dependência. O
nosso framework de injeção de dependência (DIF) entende então que:
A classe
A
faz uso de injeção de dependência, pois seu construtor foi anotado com
@Inject.
Antes de instanciar um objeto da classe
A
, o framework deve criar os objetos (depen-
dências) usados pelo construtor dessa classe. No caso, esses objetos são de classes que
implementam a interface IB.
CAPÍTULO 14. O QUE É INJEÇÃO DE DEPENDÊNCIA? 69
Mas qual classe que implementa
IB
deve ser instanciada? Para isso, o framework
consulta o arquivo de configuração e descobre que
IB
está mapeada para
B1
, conforme
explicamos anteriormente.
Então o framework instancia um objeto do tipo
B1
e um objeto do tipo
A
. Ao instanciar
esse último objeto ele também passa o objeto do tipo
B1
como parâmetro de seu
construtor.
A explicação que acabamos de apresentar foi baseada no Guice
3
, um framework de injeção de
dependências para Java, desenvolvido pelo Google. No entanto, existem outros frameworks
semelhantes, tanto para Java como para outras linguagens.
Exercícios
1. Como explicamos no Capítulo 6
4
, padrões de projeto são classificados em padrões criacionais,
estruturais e comportamentais. Em qual dessas categorias você classificaria Injeção de
Dependência? Justifique brevemente.
2. Injeção de Dependência, muitas vezes, é comparada com padrão de projeto Fábrica. Qual
a desvantagem de “injetar” dependências por meio de fábricas? Para responder, compare os
seguintes digos:
class A {
IB b ;
A( IB b ) {
this .b = b; // inje çã o de depend ê ncia
}
}
class A {
IB b ;
A () {
this .b = IB_Factory . get In stance (); // f á brica
}
}
3. Qual a relação entre Injeção de Dependência (padrão de projeto) e Inversão de Dependência
(princípio de projeto)? Para saber mais sobre Inversão de Dependência, consulte o Capítulo
5
5
.
4. Por que costuma-se dizer que Injeção de Dependência pode, em certos casos, violar a
propriedade de Ocultamento de Informação? Para ilustrar a sua resposta use como exemplo
a classe Estacionamento da Seção 5.3.1 do Capítulo 5
6
do livro.
3
https://github.com/google/guice
4
../cap6.html#introdu%C3%A7%C3%A3o
5
../cap5.html#princ%C3%ADpio-de-invers%C3%A3o-de-depend%C3%AAncias
6
../cap5.html#exemplo-1
Capítulo 15
Construindo Sistemas com uma
Arquitetura Limpa
15.1 Introdução
Arquitetura Limpa
(Clean Architecture) é um padrão arquitetural proposto por Robert
Martin mais conhecido como Uncle Bob com o objetivo de promover a implementação
de sistemas que favorecem reusabilidade de digo, coesão, independência de tecnologia e
testabilidade. Se quisermos adotar a classificação do Capítulo 7
1
, a Arquitetura Limpa pode
ser considerada como uma arquitetura em camadas.
Normalmente, Arquitetura Limpa é ilustrada por meio da seguinte figura (baseada em uma
figura semelhante do seguinte post
2
do “The Clean Code Blog”).
Figura 15.1: Arquitetura Limpa
1
../cap7.html
2
https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
70
CAPÍTULO 15. CONSTRUINDO SISTEMAS COM UMA ARQUITETURA LIMPA 71
A seguir, vamos comentar sobre cada um das camadas da arquitetura.
15.2 Entidades e Casos de Uso
No centro da arquitetura, temos as classes responsáveis pelas regras de negócio, que podem
ser de dois tipos: Entidades e Casos de Uso.
Entidades
são classes comuns a vários sistemas da empresa. Suponha, por exemplo, uma
universidade com sistemas acadêmico, financeiro, extensão, etc. Todos esses sistemas têm
que lidar com classes como
Aluno
,
Professor
,
Curso
,
Departamento
, etc. Essas classes são então
chamadas de Entidades. Além de dados, entidades podem implementar regras de negócio
genéricas. Por exemplo, uma regra da universidade define que todo
Professor
deve pertencer
a exatamente um Departamento.
as classes da camada
Casos de Uso
implementam regras de negócio específicas de
um sistema. Por exemplo, o sistema acadêmico do nosso exemplo pode ter uma classe
DiarioDeClasse
que armazena a lista de objetos do tipo
Aluno
matriculados em uma
Disciplina
que está sendo ofertada em um determinado semestre. Uma regra de negócio define que um
Aluno
somente pode ser incluído em um
DiarioDeClasse
se tiver cursado os pré-requisitos da
sua Disciplina.
Para evitar qualquer tipo de confusão, gostaríamos de comentar que os casos de uso em uma
Arquitetura Limpa não tem uma correspondência, pelo menos direta, com casos de uso para
especificação de requisitos e, menos ainda, com diagramas de casos de uso da UML, tal como
estudamos no Capítulo 3
3
.
15.3 Adaptadores
Na terceira camada, de dentro para fora, temos classes e interfaces chamadas de
Adaptadores
.
Eles têm como função mediar a interação entre a camada mais externa da arquitetura (sistemas
externos) e as camadas centrais (Casos de Uso e Entidades).
Suponha, por exemplo, que o sistema use uma API REST para comunicação com seus clientes.
As classes adaptadoras serão então responsáveis por implementar os endpoints REST da API.
Isto é, elas devem receber as requisições e encaminhá-las para os casos de uso correspondentes.
E, também, fazer o caminho inverso: receber os resultados retornados pelos casos de uso e
convertê-los em documentos JSON que serão enviados para os clientes.
15.4 Frameworks Externos
Na camada mais externa, temos as classes de bibliotecas, frameworks e quaisquer sistemas
externos. Por exemplo, é nessa camada que ficam os sistemas responsáveis por persistência
em bancos de dados, construção de interfaces com usuários, envio de mails, comunicação com
provedores de pagamento, comunicação com determinados hardware, etc.
Por exemplo, a universidade do nosso exemplo pode possuir um sistema para gerenciamento
de cursos de extensão, que aceita pagamento por meio de cartões de crédito. Para isso,
o sistema usa um serviço de terceiros, que oferece algumas classes para processamento de
pagamentos. Logo, tais classes ficam na camada mais externa de uma Arquitetura Limpa.
No livro Arquitetura Limpa
4
, veja como essa camada é descrita:
3
../cap3.html#diagramas-de-casos-de-uso
4
https://dl.acm.org/doi/book/10.5555/3175742
CAPÍTULO 15. CONSTRUINDO SISTEMAS COM UMA ARQUITETURA LIMPA 72
Todos os detalhes ficam na camada de frameworks e drivers. A Web
é um detalhe. O banco de dados é um detalhe. Mantemos essas
tecnologias na camada mais externa porque é onde elas podem
fazer menos mal.
15.5 Regra de Dependência
Em uma Arquitetura Limpa, as classes de uma camada X não devem conhecer nenhuma
classe de uma camada Y mais externa. No seu livro, Uncle Bob afirma categoricamente:
O nome de um elemento declarado em uma camada externa não
deve ser mencionado pelo digo de uma camada interna. Isso inclui
funções, classes, variáveis e qualquer outro elemento de digo.
Assim, em uma Arquitetura Limpa, as camadas centrais são mais estáveis menos sujeitas
a mudanças do que as camadas mais externas. Por exemplo, as entidades de um sistema
raramente precisam ser modificadas. Sobre os casos de uso, é verdade que eles, às vezes,
precisam ser modificados. Porém, queremos evitar que essas modificações ocorram por
mudanças nas tecnologias usadas na aplicação, como bancos de dados, frameworks e bibliotecas.
Resumindo, a Regra de Dependência garante que
entidades e casos de uso são classes
“limpas” de qualquer tecnologia ou serviço externo ao sistema.
15.6 Invertendo o Fluxo de Controle
Em uma Arquitetura Limpa, fluxos de controle de “fora para dentro” são implementados de
forma “natural”, pois eles seguem o mesmo sentido da Regra de Dependência. Por exemplo,
uma camada mais externa Y pode criar um objeto de um tipo mais interno X e então chamar
um método desse objeto.
No entanto, em alguns cenários, um caso de uso pode ter que chamar um método de uma
classe de uma camada mais externa. Para ficar claro, suponha que um caso de uso precise
enviar um mail. Antes de mais nada, vamos supor que existe no sistema uma classe, de uma
camada mais externa, chamada MailServiceImpl e com um método send:
public class Ma ilSer viceI mpl {
public void send ( String msg );
}
No entanto, esse exemplo implica em um fluxo de dentro para fora: o caso de uso tem que
declarar uma variável de uma classe de uma camada mais externa, o que contraria a regra da
dependência!
A solução consiste em ter uma interface na camada de caso de uso chamada
MailServiceInterface com um método send(String msg).
package CasosDeUso ;
public interface Mai lServ ice Int erf ace {
void send ( String msg );
CAPÍTULO 15. CONSTRUINDO SISTEMAS COM UMA ARQUITETURA LIMPA 73
}
// outras classes da camada Casos de Uso
Essa interface foi criada para funcionar como uma abstração para o serviço de envio de mail.
Ou seja, para evitar que o caso de uso tenha que se acoplar a uma classe concreta desse
serviço.
Além disso, como
MailServiceInterface
pertence à camada Caso de Uso, as outras classes
dessa camada podem chamar send sem violar a Regra de Dependência.
Prosseguindo, a classe MailServiceImpl deve implementar a interface MailServiceInterface.
import CasosDeUso . Mail Ser vic eInte rfa ce ;
public class Ma ilSer viceI mpl implements Ma ilS ervic eIn ter fac e {
public void send ( String msg ) {
// chama servi ço externo para enviar e - mail
}
}
Essa implementação não viola a Regra de Dependência, pois uma classe de uma camada mais
externa (
MailServiceImpl
) está dependendo de um elemento de digo de uma camada mais
interna. No caso, esse elemento é uma interface (MailServiceInterface).
O seguinte diagrama de classes ilustra a solução que acabamos de descrever.
Figura 15.2: Dependências em uma arquitetura limpa
15.7 Conclusão
Uma Arquitetura Limpa é uma aplicação de diversos conceitos que estudamos no Capítulo 5
5
,
incluindo propriedades de projeto como
coesão
,
acoplamento
e
separação de interesses
5
../cap5.html
CAPÍTULO 15. CONSTRUINDO SISTEMAS COM UMA ARQUITETURA LIMPA 74
e princípios de projeto como
responsabilidade única
e
inversão de dependências
. Ela
faz uso também do padrão de projeto adaptador, que estudamos no Capítulo 6
6
.
As recomendações principais de uma Arquitetura Limpa são as seguintes:
Ao implementar uma aplicação, pense nas suas Entidades, que são classes que armazenam
principalmente dados e que poderão ser reusadas em outros sistemas que vo vai
construir no futuro.
Depois, pense nos Casos de Uso, que vão implementar regras de negócio relacionadas
com as Entidades de seu sistema. Mas torne as classes que representam Entidades e
Casos de Uso “limpas” de qualquer tecnologia. Lembre-se “a Web é um detalhe; o banco
de dados é um detalhe”.
Por fim, pense nas classes Adaptadoras, que vão funcionar como portas de entrada e
saída, para comunicação entre as classes internas e o mundo externo.
Seguindo essas recomendações, você vai produzir uma arquitetura que separa dois tipos de
interesses (ou requisitos): interesses de negócio e interesses de tecnologia. E assim, será mais
fácil testar seu sistema e também adaptá-lo às novas tecnologias que, com certeza, vão surgir
no futuro.
Para Saber Mais
Veja um vídeo que gravamos sobre o conteúdo deste artigo didático.
Se quiser saber mais sobre Arquitetura Limpa, você pode também consultar o livro
7
de mesmo
nome do Uncle Bob.
E temos também um artigo didático sobre Arquitetura Hexagonal
8
.
Exercícios
1. Para fixar os principais conceitos de uma Arquitetura Limpa, responda ao seguinte
exercício
9
de V ou F, com correção online.
2. Em uma arquitetura limpa “o nome de um elemento declarado em uma camada externa
não deve ser mencionado pelo digo de uma camada interna”? Qual a principal vantagem
ou benefício dessa regra?
3. No texto do artigo, propositalmente não mencionamos a camada da classe
MailServiceImpl
.
(a)
Se quisermos ter um digo totalmente aderente aos princípios de uma Arquitetura
Limpa, por que MailServiceImpl não pode pertencer à camada de Adaptadores?
(b) Em qual camada vo implementaria então MailServiceImpl?
4. Suponha um Sistema de Bibliotecas. Um Caso de Uso desse sistema precisa obter a
lista de livros que estão emprestados para um certo usuário da biblioteca. Fisicamente, essa
informação está armazenada em um banco de dados relacional. Modele então a implementação
desse requisito assumindo que o sistema adota uma Arquitetura Limpa. Especificamente,
responda:
6
../cap6.html#adaptador
7
https://dl.acm.org/doi/book/10.5555/3175742
8
./arquitetura-hexagonal.html
9
../exercicios/exvf.html#/cap/ArqLimpa
CAPÍTULO 15. CONSTRUINDO SISTEMAS COM UMA ARQUITETURA LIMPA 75
(a)
Nesse sistema, provavelmente, existe uma classe
Livro
. Ela deve pertencer a qual
camada?
(b)
No sistema, existe também uma uma interface com métodos de negócio. Por exemplo,
um método que retorna a lista de livros emprestados para um certo usuário. Essa
interface pertence a qual camada?
(c)
Ainda sobre a interface mencionada no item anterior, existe uma classe que implementa
os seus métodos. Por exemplo, essa classe implementa o método que retorna a lista
de livros emprestados e, para isso, faz uso de consultas SQL. Essa classe deve ser
implementada em qual camada?
(d)
Para terminar, o gerenciador de banco de dados (seja ele MySQL, PostgreSQL, Oracle,
etc.) estará localizado em qual camada?
5. Suponha que um sistema use tecnologias X, Y e Z. E suponha que temos certeza de que
elas nunca vão mudar no futuro. Ou seja, não existe chance de amanhã o sistema ter que
usar uma tecnologia X’, Y’ ou Z’. Nesse cenário, você acha que ainda pode ser útil a adoção
de uma Arquitetura Limpa? Justifique.
Capítulo 16
O que é uma Arquitetura Hexagonal?
16.1 Introdução
O conceito de Arquitetura Hexagonal foi proposto por Alistair Cockburn, em meados dos anos
90, em um artigo
1
postado na primeira wiki que foi desenvolvida, chamada WikiWikiWeb
(cujos artigos tratavam principalmente de temas relacionados com Engenharia de Software).
Os objetivos de uma Arquitetura Hexagonal são parecidos com os de uma Arquitetura Limpa,
tal como descrevemos em um outro artigo
2
. Mas, para reforçar, a ideia é construir sistemas
que favorecem reusabilidade de digo, alta coesão, baixo acoplamento, independência de
tecnologia e que são mais fáceis de serem testados.
Uma Arquitetura Hexagonal divide as classes de um sistema em dois grupos principais:
Classes de domínio, isto é, diretamente relacionadas com o negócio do sistema.
Classes relacionadas com infraestrutura, tecnologias e responsáveis pela integração com
sistemas externos (tais como bancos de dados).
Além disso, em uma Arquitetura Hexagonal,
classes de domínio não devem depender
de classes relacionadas com infraestrutura, tecnologias ou sistemas externos
. A
vantagem dessa divisão é desacoplar esses dois tipos de classes.
Assim, as classes de domínio não conhecem as tecnologias bancos de dados, interfaces com
usuário e quaisquer outras bibliotecas usadas pelo sistema. Consequentemente, mudanças de
tecnologia podem ser feitas sem impactar as classes de domínio. Talvez ainda mais importante,
as classes de domínio podem ser compartilhadas por mais de uma tecnologia. Por exemplo,
um sistema pode ter diversas interfaces (Web, mobile, etc).
Em uma arquitetura hexagonal, a comunicação entre as classes dos dois grupos é mediada
por
adaptadores
, isto é, por classes que implementam o padrão de projeto de mesmo nome
que estudamos no Capítulo 6
3
. Iremos explicar melhor o papel dos adaptadores logo a seguir.
Visualmente, a arquitetura é representada por meio de dois hexágonos concêntricos (veja
figura). No hexágono interno, ficam as classes do domínio (ou classes de negócio, se você
preferir). No hexágono externo, ficam os adaptadores. Por fim, as classes de interface com o
usuário, classes de tecnologia ou de sistemas externos ficam fora desses dois hexágonos.
1
http://wiki.c2.com/?HexagonalArchitecture
2
./arquitetura-limpa.html
3
../cap6.html#adaptador
76
CAPÍTULO 16. O QUE É UMA ARQUITETURA HEXAGONAL? 77
Figura 16.1: Arquitetura Hexagonal
Assim, o nome hexagonal tem sua origem na figura acima. Cockburn justifica o uso de um
hexágono do seguinte modo:
Cada face do hexágono representa um motivo pelo qual o sistema
deve se comunicar com o mundo exterior. É por isso que são
hexágonos concêntricos e não círculos concêntricos.
Dentre os motivos que requerem comunicação com o mundo exterior podemos citar os seguintes:
interagir com seus usuários (por meio de algum tipo de interface, seja ela gráfica, Web, mobile,
terminal, etc), persistir informações, enviar informações para outros sistemas, etc.
16.2 Adaptadores e Portas
Em uma Arquitetura Hexagonal, o termo
porta
designa as interfaces usadas para comunicação
com as classes de domínio (veja que interface aqui significa interface de programação; por
exemplo, uma interface de Java).
Existem dois tipos de portas:
Portas de entrada:
são interfaces usadas para comunicação de fora para dentro, isto
é, quando uma classe externa precisa chamar um método de uma classe de domínio.
Logo, essas portas declaram os serviços providos pelo sistema, isto é, serviços que o
sistema oferece para o mundo exterior.
Portas de saída:
são interfaces usadas para comunicação de dentro para fora, isto é,
quando uma classe de domínio precisa chamar um método de uma classe externa. Logo,
essas portas declaram os serviços requeridos pelo sistema, isto é, serviços do mundo
exterior que são necessários para o funcionamento do sistema.
O importante é que
as portas são independentes de tecnologia
. Portanto, elas estão
localizadas no hexágono interior.
CAPÍTULO 16. O QUE É UMA ARQUITETURA HEXAGONAL? 78
Por outro lado, os sistemas externos, normalmente, usam alguma tecnologia, seja ela de
comunicação (REST, gRPC, GraphQL, etc), de bancos de dados (SQL, noSQL, etc), de
interação com o usuário (Web, mobile, etc), etc.
Daí a necessidade de componentes localizados no hexágono mais externo da arquitetura os
adaptadores –, os quais atuam de um dos dois modos a seguir:
Eles recebem chamadas de métodos vindas de fora do sistema e
encaminham essas chamadas para métodos adequados das portas de entrada.
Eles recebem chamadas vindas de dentro do sistema, isto é, das classes de domínio, e as
direcionam para um sistema externo, tais como um banco de dados, um outro sistema
da organização ou mesmo de terceiros.
16.3 Exemplo: Sistema de Bibliotecas
A próxima figura mostra a arquitetura hexagonal de um sistema para gerenciamento de
bibliotecas:
Figura 16.2: Arquitetura Hexagonal do Sistema de Bibliotecas
Na figura, podemos observar que os usuários acessam o sistema por meio de três interfaces:
Web, mobile e por meio de um sistema externo. Qualquer que seja a forma de acesso,
ele é sempre mediado por adaptadores. Em seguida, esses adaptadores comunicam-se com
uma porta de entrada, que define métodos para pesquisa no catálogo da biblioteca, para
realização de empréstimos, para cadastro de usuários, etc. Concretamente, esses métodos são
implementados pelas classes de domínio. No sistema em questão, essas classes incluem
Livro
,
Usuario, Bibliotecario, Emprestimo, Reserva, etc.
CAPÍTULO 16. O QUE É UMA ARQUITETURA HEXAGONAL? 79
O sistema precisa também persistir algumas informações. Para isso, ele usa uma porta de
saída, com métodos para salvar e ler dados de livros, salvar e ler dados de empréstimos, etc.
Plugado nessa porta temos um adaptador, que realiza as operações em um banco de dados
relacional.
Um sistema pode possuir várias portas de entrada e de saída (sempre localizadas no hexágono
interior, junto às classes de domínio). Em uma determinada porta, seja ela de entrada ou
de saída, podemos plugar um ou mais adaptadores, os quais ficam sempre localizados no
hexágono mais externo.
digo de Exemplo
Uma implementação mínima do Sistema de Bibliotecas, com motiva-
ção meramente didática, está disponível na IDE online Repl.it. Basicamente, implementamos
um serviço muito simples de pesquisa de livros por autor, em Java, usando ferramentas como
SparkJava (para implementar APIs REST) e SQLite (banco de dados). Vo pode ver o
digo no seguinte link
4
.
16.4 Conclusão
Em 2005, Cockburn tentou renomear o nome de sua arquitetura para
Arquitetura baseada
em Portas e Adaptadores, com a seguinte justificativa:
Eu finalmente tive esse momento “aha!” (junho de 2005) no qual
vi que as faces do hexágono [interno] são “portas”. . . e os objetos
entre os dois hexágonos são “adaptadores” e, portanto, o padrão
arquitetural consiste em uma Arquitetura Baseada em Portas e
Adaptadores. Esse nome admite uma explicação melhor do que
aquela que eu propus com o nome Arquitetura Hexagonal.
No entanto, a tentativa de renomeação não deu tão certo e a arquitetura, provavelmente,
continua sendo mais conhecida como Arquitetura Hexagonal.
Para Saber Mais
Veja um vídeo que gravamos sobre o conteúdo deste artigo didático.
Também recomendamos dois pequenos artigos do Alistair Cockburn na WikiWikiWeb. O
primeiro artigo
5
discute as propriedades principais de uma arquitetura hexagonal. o
segundo artigo
6
tenta renomeá-la para incluir os termos portas e adaptadores.
E, se quiser, veja nosso artigo sobre Arquitetura Limpa
7
.
Exercícios
1. Em uma Arquitetura Hexagonal, um adaptador é uma implementação do padrão de projeto
de mesmo nome. E as portas? Elas podem ser vistas como sendo uma implementação pelo
menos aproximada de qual padrão de projeto? Se necessário, consulte o Capítulo 6
8
para
responder.
4
https://replit.com/@engsoftmoderna/ExemploArquiteturaHexagonal
5
http://wiki.c2.com/?HexagonalArchitecture
6
http://wiki.c2.com/?PortsAndAdaptersArchitecture
7
./arquitetura-limpa.html
8
../cap6.html
CAPÍTULO 16. O QUE É UMA ARQUITETURA HEXAGONAL? 80
2. Na figura que mostra a arquitetura hexagonal do sistema de bibliotecas, por que os
adaptadores de interface externa (HTTP, GraphQL e REST) e o adaptador de persistência
(SQL) estão em faces distintas do hexágono? Eles poderiam ser desenhados na mesma face?
3. A definição do termo hexagonal é arbitrária, pois, dependendo da aplicação, ela poderia ser
chamada de quadrangular, pentagonal, heptagonal, octogonal, etc. Justifique essa afirmação.
3. A seguir, mostramos o digo de duas classes de domínio que são usadas na documentação
do Django, um conhecido framework para construção de aplicações Web em Python. O digo
mostrado define regras para mapeamento de campos de objetos dessas classes para colunas
de tabelas de um BD relacional. Esta implementação segue os princípios de uma arquitetura
hexagonal? Justifique sua resposta.
from django . db import models
class Musician ( models . Model ):
first_ na me = models . CharField ( max_le ng th =50)
last_name = models . CharField ( max_lengt h =50)
instru me nt = models . CharField ( max_le ng th =100)
class Album ( models . Model ):
artist = models . For ei gn Key ( Musician , on_delete = models . CASCADE )
name = models . CharField ( max_length =100)
re le ase_dat e = models . DateField ()
num_stars = models . Inte ge rField ()
5. Descreva, resumidamente, as diferenças entre a Arquitetura Hexagonal e a Arquitetura
Limpa (que estudamos em um outro artigo
9
didático).
9
./arquitetura-limpa.html
Capítulo 17
Consistência de Dados em
Microsserviços usando-se Sagas
17.1 Introdução
Como discutimos no Capítulo 7
1
, em arquiteturas baseadas em microsserviços, recomenda-se
que cada microsserviço possua sua própria base de dados. Ou seja, recomenda-se uma
arquitetura como a seguinte.
Figura 17.1: Microsserviços devem ter sua própria base de dados
Por outro lado, ao adotar essa recomendação, frequentemente surge o seguinte problema:
como garantir a consistência dos dados do sistema quando eles estão distribuídos em vários
microsserviços?
Para ilustrar, vamos usar um exemplo de uma loja virtual. Nessa loja, para concluir uma
venda temos que realizar duas operações:
op1: dar baixa nos itens vendidos (no microsserviço de estoque)
op2: processar o pagamento (no microsserviço de pagamento)
Essas duas operações devem constituir uma
transação
, o que vai garantir que elas serão
executadas de forma atômica. Mais especificamente, atomicidade significa que apenas dois
resultados são possíveis:
1
../cap7.html#microsservi%C3%A7os
81
CAPÍTULO 17. CONSISTÊNCIA DE DADOS EM MICROSSERVIÇOS USANDO-SE SAGAS82
Ou ambas as operações executam com sucesso
Ou nenhuma das operações é executada
Nessa explicação, executar significa ter seus efeitos registrados no banco de dados. Portanto,
o que não pode ocorrer é executar uma operação e não executar a outra, pois isso deixaria o
sistema em um estado inconsistente.
17.2 Garantindo Atomicidade
A seguir, vamos discutir as maneiras tradicionais para garantir atomicidade. Primeiro, em
bancos de dados centralizados. Depois, em bancos de dados distribuídos.
Bancos de Dados Centralizados
Quando um sistema possui uma arquitetura monolítica, normalmente temos um único banco
de dados. Nesses casos, a própria implementação do banco garante a execução atômica de
transações, por meio de comandos commit e rollback.
Podemos então usar um digo como o seguinte:
try {
op1 ();
op2 ();
commit ();
} catch ( Fa ilure ) {
rollback ();
}
Se
op1
e
op2
terminarem suas execuções com sucesso, chamamos
commit
para persistir os
resultados no banco de dados. Por outro lado, se qualquer uma das operações falhar,
chamamos rollback para retornar o banco ao seu estado inicial.
Bancos de Dados Distribuídos
Suponha, no entanto, que
op1
executa em um banco de dados e
op2
executa em um outro
banco de dados, como tipicamente ocorre no caso de arquiteturas baseadas em microsserviços.
Nesse novo cenário, não existe mais garantia automática de atomicidade.
Uma possível solução requer o uso de um protocolo que garanta atomicidade na execução
de operações distribuídas. O mais conhecido deles é chamado de
Two Phase Commit
(2PC)
. Apenas para ficar um pouco mais claro, esse protocolo normalmente é implementado
e disponibilizado pelo próprio banco de dados distribuído.
No entanto, os problemas de 2PC são bem conhecidos. Por exemplo, o protocolo tem um
custo e uma latência altos. Isso ocorre porque os processos participantes de uma transação
distribuída têm que trocar diversas mensagens, antes de chegarem a um consenso sobre o seu
resultado. E, no limite, pode-se chegar a uma situação de impasse. Isto é, a transação pode
ficar bloqueada por um tempo indeterminado no caso de queda do processo coordenador do
protocolo.
Por isso, alguns autores recomendam explicitamente que microsserviços não devem usar 2PC.
Veja, por exemplo, a recomendação de Sam Newman (link
2
):
2
https://www.oreilly.com/library/view/building-microservices/9781491950340
CAPÍTULO 17. CONSISTÊNCIA DE DADOS EM MICROSSERVIÇOS USANDO-SE SAGAS83
Eu recomendo fortemente que você evite o uso de transações distri-
buídas e de 2PC para coordenar mudanças de estado em microsser-
viços.
Por isso, começou-se a procurar alternativas mais viáveis para consistência dos dados de
microsserviços. Uma delas é o conceito de sagas, que descreveremos a seguir.
17.3 Sagas
Sagas
é um conceito antigo de bancos de dados, proposto em 1987 por Hector Garcia-Molina
e Kenneth Salem. Se quiser, veja o artigo
3
original, que é muito claro e fácil de ler.
Originalmente, o conceito foi proposto para tratar transações de longa duração. No entanto,
modernamente, ele está sendo aplicado também no contexto de arquiteturas baseadas em
microsserviços.
Uma saga é definida por meio de dois conjuntos:
Um conjunto de transações T1, T2,. . . , Tn (que devem ser executadas nesta ordem).
Um conjunto de
compensações
para cada transação, que vamos chamar de C1, C2,. . . ,
Cn. Ou seja, toda transação tem uma segunda transação que a reverte. Por exemplo,
uma transação de crédito de x reais é compensada por uma transação de débito do
mesmo valor.
Idealmente, desejamos que todas as transações Ti sejam executadas com sucesso e sequencial-
mente, começando em T1 e terminando em Tn. Esse é o caminho feliz de uma saga.
Porém, principalmente em ambientes distribuídos (como é o caso de microsserviços), pode ser
que uma transação intermediária Tj falhe, isto é:
T1 (sucesso), T2 (sucesso),. . . , Tj (falha)
Quando isso ocorre, temos então que executar as devidas compensações para as transações
anteriores. Ou seja, continuamos assim:
T1 (sucesso), T2 (sucesso),. . . , Tj (falha), Cj-1,. . . , C2, C1.
Estamos assumindo que quando Tj falha ela não registrou seus efeitos no banco de dados.
Logo, não precisamos chamar Cj, mas apenas as compensações de Cj-1 até C1.
Para concluir, vamos mostrar como seria o digo para implementar uma saga formada por
três transações:
try {
T1 ();
T2 ();
T3 ();
}
catch ( Fa il ureT1 ) {
// sem compensa ç ã o
}
catch ( Fa il ureT2 ) {
C1 ();
3
https://doi.org/10.1145/38713.38742
CAPÍTULO 17. CONSISTÊNCIA DE DADOS EM MICROSSERVIÇOS USANDO-SE SAGAS84
}
catch ( Fa il ureT3 ) {
C2 ();
C1 ();
}
Exercícios
1. Por que microsserviços não devem compartilhar um único banco de dados? Para responder,
vo pode consultar a Seção 7.4.1 do Capítulo 7
4
e também o início da Seção 7.4.
2. Qual a diferença entre uma transação distribuída e uma saga? Mais especificamente:
(a) Quando consideradas individualmente, as transações de uma saga são atômicas?
(b)
Se não houvesse compensações, as transações de uma saga, quando consideradas em
conjunto, seriam sempre atômicas?
(c)
Suponha uma transação Ti de uma saga. Uma transação T’ que não faz parte da saga
pode observar os resultados de Ti antes da execução completa da saga?
(d)
Suponha uma transação distribuída T. Uma segunda transação T’ pode observar os
resultados ainda intermediários de T?
(e)
Com sagas, temos que escrever a lógica de rollback, isto é, o digo das compensações.
O mesmo acontece com transações distribuídas? Sim ou não? Justifique.
3. Como um desenvolvedor deve proceder quando uma compensação Ci falhar (isto é, não
puder ser executada com sucesso)?
4. Qual problema de transações de longa duração é resolvido por meio de sagas? Se necessário,
consulte o segundo parágrafo da Introdução do artigo
5
que definiu o conceito.
4
../cap7.html#gerenciamento-de-dados
5
https://doi.org/10.1145/38713.38742
Capítulo 18
O que é uma Arquitetura Serverless?
18.1 Introdução
Neste artigo, iremos descrever o conceito de arquiteturas serverless, que tem sido cada vez
mais adotado por empresas de software.
Iremos começar descrevendo o contexto histórico, pois achamos que para entender os concei-
tos de serverless é importante conhecer a evolução dos procedimentos de compra, aluguel,
instalação, configuração e disponibilização de servidores de software.
Em seguida, iremos apresentar o conceito de funções serverless e mostrar um exemplo desse
tipo de função. Por fim, iremos listar algumas desvantagens de arquiteturas serverless.
18.2 Histórico
A a década de 1990, as empresas tinham que comprar servidores físicos para hospedar seus
sistemas. E também tinham que dispor de um espaço físico para acomodá-los, chamado de
data center
ou então de centro de processamento de dados, que era inclusive o termo mais
comum nessa época. Atualmente, costuma-se também chamar esse modelo de
on-premises
,
pois os servidores estão localizados nas próprias instalações da empresa que vai usar o sistema
(em inglês, a expressão on premises designa os terrenos e as edificações de propriedade de
uma empresa).
Em seguida, mas ainda na década de 1990, começaram a surgir data centers que hospedavam
servidores físicos para terceiros. Com isso, não era mais preciso ter espaços próprios para
instalar máquinas e, consequentemente, não era preciso se preocupar com instalações elétricas,
climatização, no-breaks, acesso à Internet, controle de acesso físico às máquinas, etc. Esses
data centers são chamados de
colocation
, pois no mesmo local são instalados servidores de
várias empresas.
Prosseguindo, no início dos anos 2000, surgiram as
plataformas de cloud
oferecendo
servidores virtuais para as empresas contratantes. Assim, a compra ou aluguel de servidores
físicos deixou de ser obrigatória. Isso passou a ser responsabilidade das empresas de cloud,
que alugam servidores virtuais que executam “em cima” de suas máquinas físicas.
O conceito de serverless pode ser visto como a próxima evolução desse processo. Basicamente,
a empresa que precisa desenvolver um sistema implementa um conjunto de funções nor-
malmente, chamadas de
funções serverless
ou
funções lambda
e copia as mesmas para
85
CAPÍTULO 18. O QUE É UMA ARQUITETURA SERVERLESS? 86
um sistema de cloud. Portanto, não existe mais aluguel de servidores, sejam eles virtuais ou
físicos. E as funções serverless podem ser invocadas por aplicações clientes do sistema ou
então serem automaticamente chamadas após a ocorrência de determinados eventos.
A próxima figura compara essas alternativas para uso de servidores e construção de aplicações.
Figura 18.1: Comparando On-Premises, Colocation, Cloud e Serverless
O nome serverless explica-se pelo fato de que os desenvolvedores não precisam se preocupar
com instalação, configuração e escalabilidade de servidores, sejam eles físicos ou virtuais.
Ou seja, para ficar bem claro, continuam existindo servidores, mas eles são mantidos e
configurados pelas empresas de cloud.
18.3 Modelo de Cobrança
Com serverless, paga-se pelo tempo de execução das funções serverless. Ou seja, apenas quando
ocorre um determinado evento ou chamada é que as funções são carregadas, executadas,
encerradas e cobradas do cliente.
Esse modelo de pagamento é semelhante ao de serviços utilitários, como energia elétrica. Por
exemplo, vo somente paga o que usa de eletricidade. Se não tiver nenhum uso no mês, sua
conta será zero ou então igual a um valor mínimo. E se precisar aumentar o seu consumo,
dentro de certos limites, vo tem certeza de que a companhia de eletricidade irá prover a
energia necessária.
Porém, mesmo não havendo pagamento pelo tempo de ociosidade das funções, uma solução
baseada em serverless não é sempre mais barata. Isso vai depender também dos preços
cobrados pelo provedor de cloud para execução de funções serverless e para aluguel de
servidores virtuais. E também do preço para comprar servidores físicos e mantê-los em data
centers ou em espaços de colocation.
18.4 Funções Serverless
Funções serverless têm as seguintes características principais:
CAPÍTULO 18. O QUE É UMA ARQUITETURA SERVERLESS? 87
Elas são stateless, isto é, elas não guardam qualquer estado entre uma execução e outra.
No entanto, elas podem acessar serviços externos, como bancos de dados e serviços de
envio de e-mail.
Elas executam por um intervalo de tempo máximo, normalmente, da ordem de alguns
minutos. Após esse intervalo, elas são automaticamente encerradas pela plataforma de
cloud.
Elas podem ser implementadas em uma variedade de linguagens de programação.
Exemplo
A Netlify é uma empresa de cloud que oferece um serviço serverless. A seguir
mostramos um exemplo de função serverless para a plataforma de cloud dessa empresa:
exports . handler = async ( event , conte xt ) => {
return {
status Co de : 200 ,
body : " Hello , World "
};
};
Como pode ser visto, a função retorna um objeto com dois campos: o primeiro é o digo de
status da chamada, no caso igual a 200, o que sinaliza que a requisição foi processada com
sucesso; o segundo é o corpo da resposta, o qual corresponde à string “Hello, World”
Pode-se chamar essa função por meio da seguinte URL:
https://functions.netlify.com/.netlify/functions/hello
Concluindo, para adotar uma arquitetura serverless, um cliente da Netlify deve implementar
um conjunto de funções semelhantes a essa que acabamos de mostrar e copiá-las para sua
conta na plataforma.
Na seguinte página
1
, vo encontra outros exemplos de funções serverless.
18.5 Conclusão
Para explicar as vantagens de serverless, os autores de um artigo
2
publicado em maio de 2021
na revista Communications da ACM fazem uma comparação interessante entre esse tipo de
arquitetura e linguagens de programação de alto nível:
Assim como linguagens de programação de alto nível ocultam os
detalhes de como uma CPU opera, arquiteturas serverless ocultam
diversos detalhes relativos à construção de sistemas distribuídos
confiáveis, escalonáveis e seguros.
Por outro lado, uma arquitetura serverless também possui desvantagens, tais como:
Complexidade de gerenciar uma arquitetura constituída por um grande número de
pequenas funções. Na verdade, cada função serverless é uma aplicação autônoma, no
sentido de que ela deve importar todas as dependências necessárias para a sua execução.
1
https://functions.netlify.com/playground/
2
https://m-cacm.acm.org/magazines/2021/5/252179-what-serverless-computing-is-and-should-
become/fulltext
CAPÍTULO 18. O QUE É UMA ARQUITETURA SERVERLESS? 88
Isso inclui bibliotecas de terceiros e também outros dulos da aplicação, como módulos
de domínio.
Maior latência, principalmente na primeira execução de uma função serverless. Essa
latência adicional deve-se à necessidade de criar um container para executar uma função
serverless e, em seguida, copiar o digo da função para o container criado. então a
função é executada. A literatura de serverless refere-se a esse problema como
problema
da partida a frio
(cold start problem). Ele é mais crítico na primeira execução de
uma função, pois depois o container pode ser mantido em cache por um certo tempo.
Riscos de alto acoplamento com a plataforma de cloud, tornando mais difícil uma
mudança para uma outra plataforma. Esse problema é chamado de
dependência de
fornecedores
(vendor lock-in). Analisando a função de exemplo acima, esse problema
pode não ficar claro, pois seu digo parece bastante com uma função normal em
JavaScript. Porém, o acoplamento com a plataforma de cloud surge quando a função
serverless faz uso de outros serviços oferecidos pela plataforma, como autenticação, filas
de mensagens, logging, bancos de dados, etc.
Antes de concluir, é importante mencionar que é possível, também, ter uma arquitetura
híbrida, na qual apenas alguns serviços são implementados por meio de funções serverless.
Exercícios
1. Quando usamos serverless não precisamos nos preocupar com as questões abaixo, EXCETO:
a. Planejamento de capacidade
b. Balanceamento de carga
c. Escalabilidade
d. Tolerância a falhas
e. Persistência de dados
2. Por que o termo serverless não deve ser interpretado de forma literal, isto é, como sendo
sinônimo de computação “sem servidores”?
3. Suponha uma agenda de compromissos construída usando-se uma arquitetura baseada em
funções serverless. Mostra-se abaixo uma das funções dessa aplicação, a qual retorna todos
os compromissos inseridos na agenda (esse digo foi copiado do seguinte repositório
3
). Qual
a desvantagem de serverless, conforme discutido na seção final do artigo, fica mais clara ao
analisarmos o digo dessa função? Justifique brevemente sua resposta.
'use strict ';
const AWS = require ('aws - sdk ');
const dynamoDb = new AWS . Dyna moDB . Do cumen tClie nt ();
module . exports = ( event , callback ) => {
const params = {
TableName : ' todos ',
};
return dyna moDb . scan ( params , ( error , data ) = > {
if ( error ) {
callback ( error );
3
https://github.com/pmuens/serverless-book
CAPÍTULO 18. O QUE É UMA ARQUITETURA SERVERLESS? 89
}
callback ( error , data . Items );
});
};
4. Analise e estude os diagramas de sequência mostrados nas figuras 1 e 2 da página 4 do
seguinte artigo
4
. Eles são de uma aplicação que inicialmente estava implementada usando
uma arquitetura monolítica (figura 1) e que então foi migrada para usar funções serverless
(figura 2). Basicamente, a funcionalidade mostrada permite que um usuário faça o upload de
um arquivo e solicite a sua conversão para um outro formato (pdf, svg, etc). Em seguida,
responda:
(a) Qual dos dois diagramas é mais simples e fácil de entender? Justifique sua resposta.
(b)
Descreva uma diferença importante entre os dois diagramas. Ou seja, descreva uma
mudança relevante que teve que ser realizada na estrutura original da aplicação (mostrada
na figura 1) para torná-la adequada para uma arquitetura baseada em serverless
(conforme mostrado na figura 2). Justifique sua resposta.
4
https://www.doc.ic.ac.uk/~rbc/papers/fse-serverless-17.pdf
Capítulo 19
Alguns Exemplos Reais e
Interessantes de Testes
19.1 Introdução
No Capítulo 8
1
, explicamos os principais conceitos de testes de software e mostramos pelo
menos uma dezena de exemplos de digo.
Neste artigo, vamos ampliar essa lista de exemplos,
mostrando e explicando exemplos
reais de testes implementados em sistemas de digo aberto
. Achamos isso relevante,
pois conhecer, ler e entender esses exemplos pode ajudá-lo na escrita de seus próprios testes.
Para ir direto para os exemplos de cada sistema, use os seguintes links:
Google Guava
Spring PetClinic
JUnit
Vue.js
Microsoft PowerToys
19.2 Guava
Guava
2
é uma biblioteca de digo aberto implementada pelo Google que oferece um
conjunto de funções básicas para concorrência, cache, E/S e grafos. Ela inclui também funções
utilitárias para trabalhar com tipos primitivos. Logo, o sistema pode ser visto como um
complemento para a biblioteca padrão de Java.
mostramos um exemplo de teste do Guava no Capítulo 8
3
, mas vamos aqui mostrar mais
três exemplos.
Exemplo 1: Ints.contains
Guava implementa uma classe chamada
Ints
4
que oferece métodos estáticos para trabalhar
com valores inteiros. Dentre eles, temos o seguinte método:
1
../cap8.html
2
https://github.com/google/guava
3
../cap8.html#n%C3%BAmero-de-assert-por-teste
4
https://guava.dev/releases/snapshot-jre/api/docs/
90
CAPÍTULO 19. ALGUNS EXEMPLOS REAIS E INTERESSANTES DE TESTES 91
public static bool ean contains ( int [] array , int target )
Ele verifica se um vetor de inteiros (array) contem um determinado inteiro (target).
O teste desse método é o seguinte:
public void te stConta ins () {
as se rt False ( Ints . contains (EMPTY , ( int ) 1));
as se rt False ( Ints . contains (ARRAY1 , ( int ) 2));
as se rt False ( Ints . contains ( ARRAY234 , ( int ) 1));
assert Tr ue ( Ints . contains (new int [] {( int ) -1}, ( int ) -1));
assert Tr ue ( Ints . contains ( ARRAY234 , ( int ) 2));
assert Tr ue ( Ints . contains ( ARRAY234 , ( int ) 3));
assert Tr ue ( Ints . contains ( ARRAY234 , ( int ) 4));
}
Esse teste é quase que auto-explicativo. Na verdade, basta entender que os identificadores em
maiúsculo são constantes definidas na classe de teste, chamada IntsTest
5
, da seguinte forma:
private static final int [] EMPTY = {};
private static final int [] ARRAY1 = {( int ) 1};
private static final int [] ARRAY23 4 = {( int ) 2 , ( int ) 3 , ( int ) 4};
Exemplo 2: Ints.reverse
Vamos aproveitar e mostrar mais um teste da classe
Ints
, desta vez do método
reverse
, que
inverte um vetor de inteiros.
public void te stReverse () {
te st Re verse ( new int [] {}, new int [] {});
te st Re verse ( new int [] {1} , new int [] {1});
te st Re verse ( new int [] {1, 2} , new int [] {2 , 1});
te st Re verse ( new int [] {3, 1 , 1} , new int [] {1 , 1 , 3});
te st Re verse ( new int [] { -1, 1, -2, 2}, new int [] {2 , -2 , 1, -1});
}
Esse método de teste sempre chama um segundo método, também chamado
testReverse
, mas
que recebe como parâmetro dois vetores de inteiros, sendo que um é o inverso do outro. Veja
seu digo:
private static void te stReverse ( int [] input , int [] e xpect edOut put ) {
input = Arrays . copyOf ( input , input . length );
Ints . reverse ( input );
assert Tr ue ( Arrays . equals ( expect edOutput , input ));
}
O código é bem simples: primeiro cria-se uma cópia do vetor
input
(basicamente, para o teste
não alterar seus elementos quando retornar); depois chama-se o método de teste (
Ints.reverse
)
e verifica-se se o resultado é aquele esperado, isto é, expectedOutput.
5
https://github.com/google/guava/blob/master/guava-tests/test/com/google/common/primitives/Ints
Test.java
CAPÍTULO 19. ALGUNS EXEMPLOS REAIS E INTERESSANTES DE TESTES 92
Exemplo 3: Files.copy
Guava disponibiliza uma classe
Files
, com métodos utilitários para trabalhar com arquivos.
Dentre eles, temos um método
copy(File from, File to)
que copia todos os bytes de um
arquivo para outro.
Um dos testes desse método é o seguinte:
public void te stCopyF ile () throws I OE xception {
File i18nFile = ge tT estFile (" i18n .txt " ); // setup
File temp = crea teTem pFile ();
Files . copy ( i18nFile , temp ); // m é todo que est á sendo te stado
as se rtEqual s ( I18N , Files . toString ( temp , C harsets . UTF_8 ));
}
Primeiro, abre-se um arquivo que existe no diretório de teste, chamado
i18n.txt
. Em
seguida, cria-se um arquivo vazio, também no diretório de teste. Ambas as tarefas são
realizadas por funções utilitárias: getTestFile e createTempFile.
Depois, o método
copy
é chamado para copiar o conteúdo de um dos arquivos (
i18nFile
) para
o outro arquivo (temp).
Por fim, temos um
assert
. Ele verifica se o conteúdo do arquivo
temp
de fato passou a ser o
conteúdo previamente lido de
i18n.txt
. Esse conteúdo é conhecido e está armazenado em
uma constante string chamada I18N.
Em tempo, a função
Files.toString
usada no
assertEquals
o conteúdo de um arquivo e
retorna-o em uma string.
Antes de terminar, queremos ressaltar que esse é um
teste de integração
, pois ele faz acesso
ao disco para ler e gravar arquivos.
19.3 Spring PetClinic
Spring PetClinic
6
é uma aplicação de demonstração do Spring
7
, um framework para desenvol-
vimento Web para Java. A PetClinic implementa um sistema simples para controle de uma
clínica veterinária. Por exemplo, o sistema armazena informações sobre os animais internados
na clínica e seus respectivos donos.
O sistema tem alguns
testes de integração
interessantes, que se beneficiam de funcionali-
dades oferecidas de forma transparente pelo Spring.
Por exemplo, a classe ClinicServiceTests
8
implementa um método que testa o serviço de
atualização do nome do dono de um animal:
@Test
@T ransa ctional
void shoul dUp dateO wne r () {
6
https://github.com/spring-projects/spring-petclinic
7
https://spring.io/projects/spring-framework
8
https://github.com/mtov/spring-petclinic/blob/main/src/test/java/org/springframework/samples/pe
tclinic/service/ClinicServiceTests.java
CAPÍTULO 19. ALGUNS EXEMPLOS REAIS E INTERESSANTES DE TESTES 93
Owner owner = this . owners . findById (1);
String oldLa st Na me = owner . getL as tName (); // setup
String newLa st Na me = o ldLastNam e + " X ";
owner . set La stName ( n ewLastNam e );
this . owners . save ( owner ); // mé todo que est á sendo testado
owner = this . owners . findById (1);
assert Th at ( owner . g etLastNam e ()). isE qu al To ( ne wLastName );
}
Esse teste recupera o nome do dono cujo ID é 1. Em seguida, ele altera esse nome acrescentando
um “X” no final e salva a alteração no banco de dados. Para finalizar, ele recupera de novo o
mesmo nome e verifica se ele agora termina com um “X”.
Esse teste é um teste de integração, pois ele recupera e salva dados no banco de dados da
aplicação. Além disso, ele usa dois serviços interessantes do Spring:
O valor do atributo
owners
com métodos para recuperar e salvar dados de donos
de animais é injetado automaticamente pelo Spring, que também implementa um
serviço de
injeção de dependências
. Para isso, a declaração desse atributo na classe
ClinicServiceTests possui uma anotação @Autowired:
class Cl inicS erv iceTests {
...
@Autow ir ed // Spring vai injetar essa depend ê ncia
protected O wne rR epo si tor y owners ;
...
}
Para saber mais sobre injeção de dependência, você pode consultar esse artigo
9
do nosso site.
O teste é também anotado com
@Transactional
para garantir uma semântica de transa-
ções na sua execução. No caso de testes de integração, essa anotação está configurada
para realizar automaticamente um rollback ao final de cada transação. Veja o que diz
um comentário na própria classe de teste:
each test method is executed in its own transaction, which is
automatically rolled back by default. Thus, even if tests insert or
otherwise change database state, there is no need for a teardown
or cleanup script.
19.4 JUnit
O JUnit também possui testes, que, evidentemente, são executados pelo próprio JUnit. As
primeiras versões desses testes foram implementadas por Kent Beck e Erich Gamma, criadores
do JUnit e, em grande medida, do próprio conceito de testes de unidade automatizados.
Os testes do JUnit versão 4 ficam em um diretório chamado test
10
, que fica “ao lado” do
diretório principal (main) com o código do sistema.
9
./injecao-dependencia.html
10
https://github.com/mtov/junit4/tree/main/src
CAPÍTULO 19. ALGUNS EXEMPLOS REAIS E INTERESSANTES DE TESTES 94
Alguns testes interessantes estão implementados em SuiteTest.java
11
. A primeira versão dessa
classe foi implementada por Erich Gamma, em dezembro de 2000 (confira o commit
12
). Os
testes implementados nela funcionam conforme descrito a seguir.
Primeiro, no mesmo diretório de
SuiteTest.java
, existem arquivos que implementam alguns
casos de teste. Vamos mostrar um deles (os comentários foram acrescentados por nós mesmos):
// OneTestC as e . java
public class OneTes tC ase exten ds TestCase {
public void no Te st Case () { // n ão come ç a com test
}
public void testCase () { // ok , mé todo de teste !
}
public void testCase ( int arg ) { // possui par â metro
}
}
Esse arquivo declara um
TestCase
simples, que usa a convenção antiga do JUnit para imple-
mentação de testes. Segundo essa convenção: (1) casos de teste devem ser implementados em
subclasses de
TestCase
; (2) cada teste deve começar com o prefixo
test
, não possuir parâmetro
e retornar void.
Logo,
OneTestCase
é um caso de teste que possui apenas um método de teste válido (
testCase()
),
cujo corpo é vazio. Essa propriedade de
OneTestCase
é testada pelo seguinte método de
SuiteTest:
public void testO neTes tCase () {
TestSuite t = new T es tS ui te ( On eTestCase . class ); // setup
t. run ( fResul t ); // m é todo que est á sendo te stado
assert Tr ue ( fResult . runCoun t () == 1);
assert Tr ue ( fResult . fa il ureCoun t () == 0);
assert Tr ue ( fResult . errorC ou nt () == 0);
assert Tr ue ( fResult . wa sSucc es sful ());
}
Para ficar bem claro: como JUnit é um framework de teste, os seus métodos de teste rodam
testes implementados em algum outro arquivo e verificam se o resultado é aquele esperado.
Esses testes são interessantes porque neles o JUnit tem um duplo papel: ele é tanto o
framework de testes como o SUT, isto é, o sistema que está sendo testado.
Como mostrado no digo acima,
testOneTestCase
executa o teste implementado na classe
OneTestCase
(duas primeiras linhas). Ao executar o teste, passa-se como parâmetro um objeto
(
fResult
) que vai armazenar o resultado da execução. Para ficar mais claro,
fResult
é do tipo
TestResult e foi inicializado no setUp do teste.
Por fim, são usados quatro comandos assert para assegurar que:
A suíte de teste que foi executada possui um único caso de teste (subclasse de
TestCase
);
11
https://github.com/mtov/junit4/blob/main/src/test/java/junit/tests/framework/SuiteTest.java
12
https://github.com/mtov/junit4/commit/b6a0693454ac8ded32b3a1ea7c859c5a840169dc
CAPÍTULO 19. ALGUNS EXEMPLOS REAIS E INTERESSANTES DE TESTES 95
Dentre os métodos de teste executados, nenhum deles falhou.
E, também, não ocorreu nenhum erro ou exceção;
Por fim, o teste como um todo foi bem sucedido.
19.5 Vue.js
Vue.js
13
é um framework JavaScript para implementação de single-page applications
14
. O
sistema permite criar componentes que possuem dados, operações e também uma apresentação
em HTML, isto é, a parte visual do componente que será vista no browser.
Veja um teste do Vue.js, implementado usando o framework de testes Jest.
it ( ' chained usage ' , () => {
const vm = new Vue ({
template : ' <div >{{ msg | upper | reverse }} </ div > ',
data : {
msg : ' hi '
},
filters : {
upper : v = > v . to UpperCase () ,
reverse : v = > v. split ( ' '). rever se (). join ( ' ')
}
}). $mount ()
expect ( vm. $el . t extConten t ). toBe ( ' IH ')
})
Primeiro, instancia-se (
new
) e exibe-se (
mount
) um componente Vue, que possui um
template
HTML, dados (
msg
) e dois filtros. O primeiro filtro (
upper
) converte uma string para maiúsculo
e o segundo (
reverse
) converte uma string para um vetor de caracteres, inverte a ordem deles
e concatena de volta os resultados.
Esses filtros são usados no template do componente da seguinte forma (a qual lembra pipes
de linhas de comando do Unix):
msg | upper | reverse
Como
msg
é a string
hi
, o teste espera (
expect
), na sua última linha, que o texto exibido pelo
componente seja igual a IH.
19.6 Microsoft PowerToys
PowerToys é um conjunto de utilitários Windows. Por exemplo, um dos utilitários permite
fixar uma janela para que ela apareça “por cima” de qualquer outra janela do sistema.
O digo desses utilitários, implementados em C#, está público em um repositório
15
do
GitHub. E eles possuem um conjunto interessante de
testes end-to-end
implementados
usando dois frameworks: Apium e WinAppDriver.
13
https://vuejs.org/
14
https://engsoftmoderna.info/cap7.html#exemplo-single-page-applications
15
https://github.com/microsoft/PowerToys
CAPÍTULO 19. ALGUNS EXEMPLOS REAIS E INTERESSANTES DE TESTES 96
Exemplo 1: FancyZones
Primeiro, mostramos um teste do utilitário FancyZones, que “é um gerenciador de janelas
que facilita a criação de layouts de janela complexos e o posicionamento rápido de janelas
nesses layouts”.
private void Save Ch anges ()
{
string isEnabled = _s aveButton . Ge tAttrib ute (" IsEnabled " );
Assert . AreEqu al (" True ", isEnabled );
_sav eB utton . Click ();
isEnabled = _saveBut to n . GetAtt ribute (" IsEnabled " );
Assert . AreEqu al (" False " , isEnabled );
}
Inicialmente, testa-se se o botão “Save” do utilitário está disponível (enabled). Então, simula-
se um click no mesmo. Feito isso, o botão não deve estar mais habilitado, pois acabamos de
realizar um salvamento e, portanto, não faz sentido salvar de novo.
Exemplo 2: Menu de Configuração
O segundo teste simula a abertura do menu de configuração do PowerToys, o qual fica na
barra de ícones do Windows. Os comentários presentes no teste ajudam bastante a entender
o seu funcionamento.
[ TestMethod ]
public void Set tin gsO penW ith Con tex tMe nu ()
{
// open tray
trayBu tt on . Click ();
Wa it Se conds (1);
is Tr ayOpene d = true ;
// open P ow er Toys co ntext menu
Ap piumW ebE lemen t pt = Pow erToy sTr ayB utt on ();
Assert . IsNotNull ( pt );
new Actions ( sessi on ). MoveTo El ement ( pt ). Co ntextCl ic k (). Perform ();
// open settings
session . Fi ndE lem entBy XPa th (" // MenuItem [ @Name =\" Se ttings \"] " ). Click ();
// check settin gs window opened
Wi ndows Element s ettin gsWin do w =
session . Fi ndE lemen tBy Name (" PowerToys Settings " );
Assert . IsNotNull ( sett ingsW indow );
is Setti ngs Opene d = true ;
}
CAPÍTULO 19. ALGUNS EXEMPLOS REAIS E INTERESSANTES DE TESTES 97
Exercícios
1. Spring PetClinic: estude e documente o funcionamento do método de teste shouldInser-
tOwner()
16
, também da classe ClinicServiceTests.
2. JUnit: estude e documente o funcionamento do teste testInheritedTests
17
, que testa a
execução do seguinte teste
18
.
16
https://github.com/mtov/spring-petclinic/blob/main/src/test/java/org/springframework/samples/pe
tclinic/service/ClinicServiceTests.java#L102
17
https://github.com/mtov/junit4/blob/main/src/test/java/junit/tests/framework/SuiteTest.java#L44
18
https://github.com/mtov/junit4/blob/main/src/test/java/junit/tests/framework/InheritedTestCase.j
ava
Capítulo 20
Duas Recomendações para
Manutenção de Software
20.1 Introdução
Neste artigo, vamos apresentar duas recomendações (ou “dicas”), propostas originalmente
para outras áreas, mas que se aplicam a manutenção de software. Essas recomendações são
conhecidas pelos seguintes nomes:
Teoria das Janelas Quebradas
Regra dos Escoteiros
Além de apresentar ambos os conceitos, vamos mostrar que existe uma conexão entre eles.
20.2 Teoria das Janelas Quebradas
A Teoria das Janelas Quebradas é famosa entre criminologistas, policiais e autoridades da
área de segurança. Proposta em 1982 por dois criminologistas James Wilson e George
Kelling –, ela defende o seguinte:
Se uma das janelas de uma casa for quebrada e não for consertada, as
pessoas que passam na rua podem achar que a casa está abandonada
e que ninguém se importa com ela. Então, alguém pode decidir
jogar uma pedra e quebrar mais alguns vidros. . . Depois de um
tempo, muitas janelas podem estar quebradas, o telhado pode
também começar a ter problemas e uma sensação de abandono vai
tomar conta da casa e até mesmo de sua vizinhança.
A mensagem que a teoria tenta passar é que devemos ter “tolerância zero” com problemas
aparentemente pequenos. Caso contrário, eles podem se acumular, criando uma espiral de
abandono e descontrole.
Apesar de mais discutida entre psicólogos sociais, a Teoria das Janelas Quebradas aplica-se
também a manutenção de software. Um dos primeiros livros a fazer essa conexão foi “O
Programador Pragmático”, de Andrew Hunt e David Thomas. Veja o que os autores dizem:
98
CAPÍTULO 20. DUAS RECOMENDAÇÕES PARA MANUTENÇÃO DE SOFTWARE99
Figura 20.1: Exemplo de casa abandonada e com janelas quebradas. Créditos: link. Licença
CC0 (domínio público).
Não deixe “janelas quebradas” (projetos ruins, decisões erradas ou
digo ruim) sem reparos. Conserte cada um desses problemas as-
sim que ele for descoberto. Nós vimos sistemas bem projetados e
funcionais se deteriorarem rapidamente quando as janelas começam
a quebrar. Existem diversos fatores que podem contribuir para a
deterioração de um sistema de software, mas a negligência acelera
esse processo mais rapidamente do que qualquer outro fator.
A seguir, listamos mais alguns exemplos de “janelas quebradas” que não devem ser toleradas
quando almeja-se produzir e manter software com qualidade:
digo com bugs conhecidos e graves, mas que mesmo assim entram em produção.
digo que não segue convenções de indentação e regras de nomes para identificadores.
digo que não possui testes.
digo que viola a arquitetura planejada do sistema; por exemplo, que faz chamadas
SQL fora do dulo de persistência.
digo com code smells, tais como métodos longos, uso injustificado de variáveis globais,
digo duplicado, etc.
digo com problemas relevantes de desempenho.
digo com vulnerabilidades graves de segurança.
etc
20.3 Regra dos Escoteiros
A Regra do Escoteiro foi descrita por Robert Martin no seu livro “Código Limpo”. Segundo
ele, os escoteiros norte-americanos têm uma regra simples que pode ser adaptada para o
contexto de manutenção de software. A regra é a seguinte:
CAPÍTULO 20. DUAS RECOMENDAÇÕES PARA MANUTENÇÃO DE SOFTWARE100
Deixe o acampamento mais limpo do que vo o encontrou.
Ou seja, se você está corrigindo um bug em um sistema ou implementando uma nova
funcionalidade e se depara com uma “janela quebrada” isto é, com algum digo de
qualidade você não deve simplesmente fechar os olhos. Em vez disso, procure refatorar esse
digo, para entregá-lo mais “limpo” do que o encontrou. Vo deve fazer isso mesmo que
não seja o autor do digo com problema.
A Regra do Escoteiro relaciona-se com o conceito de
refactorings oportunistas
que estu-
damos no Capítulo 9 do livro. A ideia é que refactorings devem ser uma atividade contínua,
realizada “no meio” de atividades de correção de bugs e de implementação de novas funciona-
lidades.
De fato, em um trabalho que publicamos em 2016 sobre motivações para refactorings (link),
diversos desenvolvedores nos reportaram que têm a preocupação de deixar o digo mais
limpo, após uma tarefa de manutenção. Eles não mencionaram explicitamente a Regra dos
Escoteiros, mas suas respostas estão perfeitamente alinhadas com o propósito da regra. Veja
exemplos de respostas:
Quando eu estava corrigindo uma exceção, eu percebi que deveria
adicionar o mesmo digo em dois lugares. Em vez de fazer isso,
eu extraí esse digo para um método e apliquei a correção apenas
nele.
O método [em que eu estava trabalhando] era muito grande e não
cabia na minha tela. Então eu extraí uma parte dele.
Esses refactorings foram aplicados para reusar digo. Eu sempre
tento fazer isso, porque quando existe muita redundância torna-se
terrivelmente mais complicado manter o digo no futuro.
Exercícios
1. Qual o nome do refactoring aplicado nas duas primeiras respostas de desenvolvedores
mencionadas na seção sobre a Regra dos Escoteiros.
Capítulo 21
Revisão de digo: Uma Breve
Introdução
Artigo de autoria de
Aline Torres
e
Marco Tulio Valente
. Elaborado como parte da
Monografia de Sistemas de Informação da primeira autora no DCC/UFMG.
21.1 Introdução
Revisão de digo é uma das práticas mais importantes para garantir a saúde a médio e
longo prazo da base de digo de um sistema. Ela é hoje adotada por várias empresas
que desenvolvem software. Por exemplo, o Stack Overflow Survey de 2019 incluiu uma
pergunta sobre o emprego da prática (veja figura). Dentre os mais de 70 mil desenvolvedores
que responderam a essa pergunta,
apenas 23% não usavam revisão de digo
no seu
trabalho (link
1
).
Figura 21.1: Resultados de pergunta do Stack Overflow survey sobre a adoção de revisão de
digo
A ideia de revisão de digo é simples: todo digo desenvolvido por um desenvolvedor tem
que ser, em seguida, analisado por pelo menos um outro desenvolvedor, chamado de
revisor
.
1
https://insights.stackoverflow.com/survey/2019#development-practices
101
CAPÍTULO 21. REVISÃO DE CÓDIGO: UMA BREVE INTRODUÇÃO 102
O revisor pode adicionar comentários no digo sob revisão, procurando esclarecer dúvidas,
sugerindo melhorias, indicando bugs, etc.
Assim, estabelece-se um “diálogo” na forma de uma troca de comentários entre o
autor
do digo e o seu revisor. Como resultado, o autor pode modificar sua implementação para
atender a alguma sugestão do revisor que ele achou procedente. Ou então, ele pode replicar e
justificar que ela não faz sentido. . . Espera-se que após esse “diálogo” o digo seja aprovado
pelo revisor e possa ser integrado no repositório do projeto.
21.2 Pull Requests
Nesta seção, vamos apresentar um breve tutorial sobre como revisar digo usando o recurso de
Pull Requests (PR)
do GitHub. Por meio desse recurso, desenvolvedores podem submeter
digo para ser integrado no repositório principal de um projeto. No entanto, antes da
integração, o GitHub permite que outros desenvolvedores revisem esse digo.
O exemplo a seguir usa a classe
Estacionamento
que foi usada no Capítulo 5
2
do livro.
Especificamente, vamos supor que uma autora criou um PR, no qual ela sugere adicionar tal
classe no sistema. Após isso, teve início o processo de revisão, que você pode acompanhar
também pela próxima figura.
Comentário 1
: o revisor comentou que alguns atributos da classe
Estacionamento
poderiam ser privados. E sugeriu também implementar um método
estaciona
para
mediar o acesso a eles.
Comentário 2:
a autora concordou com a sugestão do revisor e atualizou o seu digo
com a modificação sugerida.
Comentário 3:
o revisor aprovou o PR. A mensagem LGTM significa Looks Good to
Me. Então, o PR pode ser integrado no branch principal do projeto. Isso pode ser feito
pela própria autora, caso ela tenha permissão de escrita nesse branch.
Figura 21.2: Exemplo de revisão de digo realizada em um PR
2
../cap5.html#exemplo-1
CAPÍTULO 21. REVISÃO DE CÓDIGO: UMA BREVE INTRODUÇÃO 103
21.3 Motivações
Em 2013, Alberto Bacchelli e Christian Bird realizaram um estudo sobre revisão de digo com
873 desenvolvedores e testadores da Microsoft (link
3
). A próxima figura ilustra as principais
motivações para realização de revisão de digo, na opinião dos participantes do estudo.
Figura 21.3: Resultados de survey com desenvolvedores da Microsoft sobre as motivações
para realização de revisões de digo
Conforme respondido pelos participantes, a principal motivação é encontrar bugs no digo
submetido. Porém, outros fatores também são importantes, como (1) melhorar o digo, (2)
propor soluções alternativas e (3) transferir conhecimento. Esse último benefício pode ocorrer
nos dois sentidos, isto é, o autor pode aprender com os comentários do revisor e vice-versa.
Apenas reforçando o terceiro ponto acima, revisão de digo tem um benefício importante
que é evitar a formação de “ilhas de conhecimento” em um projeto. Em vez disso, ela ajuda
a socializar o conhecimento sobre o digo e a criar uma cultura de colaboração e troca de
ideias entre os membros do time.
21.4 O que Revisar?
Segue uma lista de questões que devem ser apontadas e consideradas em revisões de digo:
Bugs em geral
digo mais complexo do que o necessário
digo que usa um algoritmo e/ou estrutura de dados menos eficiente
digo que viola princípios de projeto (veja mais no Capítulo 5
4
)
digo que viola a arquitetura de camadas do sistema (veja mais no Capítulo 7
5
)
digo que não trata exceções e erros
digo com code smells (veja mais no Capítulo 9
6
)
3
https://doi.org/10.1109/ICSE.2013.6606617
4
../cap5.html#solid-e-outros-princ%C3%ADpios-de-projeto
5
../cap7.html#arquitetura-em-camadas
6
../cap9.html#code-smells
CAPÍTULO 21. REVISÃO DE CÓDIGO: UMA BREVE INTRODUÇÃO 104
Otimizações prematuras
Ausência de testes
Ausência de documentação, principalmente aquela mais relevante
Falhas de segurança ou privacidade
Problemas de desempenho
Problemas de usabilidade com o usuário
Uso inadequado ou sub-ótimo de APIs
Uso de bibliotecas ou frameworks não autorizados
Problemas relacionados com alocação de memória dinâmica
Problemas relacionados com programação concorrente
digo com problemas de leiaute ou indentação
digo que viola convenções de nome
21.5 Recomendações para Revisores
Nesta seção, vamos listar algumas recomendações sobre como se “portar” em uma revisão de
digo.
Para chegar até elas, partimos do artigo How to Give Respectful and Constructive Code
Review Feedback, de autoria de Michaela Greiler (link
7
). Em seguida, fizemos uma análise de
mais de 120 PRs de projetos de digo aberto, hospedados no GitHub, buscando por casos
concretos de comentários que seguem as recomendações que coletamos inicialmente. Ao todo,
analisamos pelo menos 259 comentários para selecionar os exemplos que iremos mostrar a
seguir.
Recomendações Genéricas
Vamos começar com algumas recomendações genéricas:
1.
Revisores sempre devem lembrar que o objetivo da revisão é
detectar problemas
inequívocos
no digo submetido. Por exemplo, é natural que o revisor pudesse ter
optado por resolver o mesmo problema de um modo diferente. . . Porém, a revisão não
é para propor alternativas que não tenham vantagens claras e inequívocas. Ou seja, um
revisor somente deve propor uma alternativa se ela for, de fato, bem melhor!
2.
Na linha da recomendação anterior,
evite comentários subjetivos e relacionados
a estilos pessoais
. Um exemplo são nomes de variáveis. A não ser que o nome de
uma variável seja muito ruim, recomenda-se não iniciar um debate sobre ele.
3.
Em suas revisões,
nunca use palavras ofensivas
, sarcásticas ou mesmo irônicas. Em
vez disso, seja sempre educado e profissional.
4.
Sempre
restrinja seus comentários ao digo
que foi submetido e evite tratar de
assuntos pessoais ou de outras questões.
Recomendações Específicas
Seguem agora algumas recomendações mais específicas:
5.
Nos seus comentários,
procure fazer perguntas e não julgamentos
. Segue um
exemplo de pergunta real feita por um revisor de um dos projetos que estudamos: Is
this actually used? Or is it something necessary to make the template magic work?
7
https://www.michaelagreiler.com/respectful-constructive-code-review-feedback
CAPÍTULO 21. REVISÃO DE CÓDIGO: UMA BREVE INTRODUÇÃO 105
6.
Se você tiver feito um comentário errado ou sem sentido,
reconheça o seu erro
e
agradeça, como no seguinte exemplo: Ah, I see the point. Thanks for your explanation.
7.
Sempre que possível,
use emojis
, pois eles deixam a linguagem mais coloquial e
amigável.
8.
Sempre que for esclarecedor,
referencie a documentação
interna ou externa ao
projeto. Isso vai ajudar a embasar seus comentários, como neste exemplo: [a given
feature] allows id as in [URL]
9.
Não deixe de
elogiar o digo
submetido, se achar que ele possui um nível de qualidade
superior, como neste caso: Thanks for the clear test instructions.
10.
Se necessário,
use imagens e screenshots
para explicar sua dúvida. Veja um exemplo
na próxima figura. Por meio dela, o revisor quis deixar claro que o digo submetido
poderia ter um comportamento errado quando um determinado filtro fosse definido
como All.
Figura 21.4: Exemplo de screenshot adicionado por um revisor em sua revisão
11.
Procure
justificar os seus comentários
quando a motivação não for clara ou quando
o autor do digo for um desenvolvedor júnior. Veja um exemplo: Sugiro trocar o
ArrayList de Aluno por um HashMap<String, Aluno>, porque podemos verificar de
forma mais eficiente se um determinado Aluno está presente na sua turma. . . .
12.
Sempre que for razoável,
use o pronome “nós”
ou a expressão “a gente”, em vez
de usar o pronome “você”, pois assim você deixa claro que estão autor e revisor
trabalhando juntos. Por exemplo, em vez de dizer, será que você não poderia tornar
esse atributo privado?, você pode perguntar: será que a gente não poderia tornar esse
atributo privado?
13.
Por último, mas não menos importante, se você tiver uma divergência muito forte em
relação ao código submetido (por exemplo, se achar que está tudo errado. . . ) ou se a
troca de comentários não estiver convergindo para aprovar o PR, tente
agendar uma
reunião com o autor
para expor sua visão e tentarem chegar a um consenso. Porém,
essa interação síncrona deve ser uma exceção e reservada para casos mais importantes.
CAPÍTULO 21. REVISÃO DE CÓDIGO: UMA BREVE INTRODUÇÃO 106
21.6 Recomendações para Autores
Os autores também devem ser profissionais e educados em suas respostas. Eles devem entender
também que
revisão de digo não é um exame de proficiência
. Ou seja, como autor,
não leve a revisão para o lado pessoal e nunca imagine que o revisor está julgando sua
competência.
Outro ponto muito importante é que os autores devem submeter PRs pequenos, caso queiram
obter uma resposta rápida e mais proveitosa dos revisores. Por exemplo, os autores do livro
Software Engineering at Google, recomendam que
um PR deve ter no máximo 200 linhas
de digo.
Se PRs forem muito grandes, existe também a chance de a qualidade da revisão cair muito,
como sumarizado no tweet deste engenheiro de software:
Ask a programmer to review 20 lines of code, they’ll find 7 issues. Ask them to review 500
lines & they’ll find 0 issues.
Hays Stanford (@haysstanford) March 10, 2021
21.7 Automatizando a Revisão
Antes de concluir, gostaríamos de comentar que diversos “problemas” tratados em uma revisão
de digo podem ser detectados de forma automática por meio de
ferramentas de análise
estática
. Por exemplo, questões como convenções de nomes de identificadores (camel case,
snake case, etc), leiaute de digo e estilo de indentação (tab ou espaços, por exemplo) podem
ser padronizadas por meio de
linters
. Com isso, evita-se que um revisor humano tenha que
perder tempo com tais questões.
Exercícios
1. Qual a diferença principal entre revisão de digo e programação em pares?
2. Descreva uma desvantagem de revisão de digo.
3. Seja o seguinte digo de uma classe
Stack
. Quais comentários você faria caso fosse
responsável pela revisão do código dessa classe? Sugestão: existem quatro questões sobre esse
digo que achamos que deveriam ser mencionadas em uma revisão.
Se preferir, você pode fazer sua revisão em um PR do GitHub. Ou seja, abra um PR em um
de seus repositórios com o digo da classe
Stack
. Se não sabe fazer isso, leia antes a seção
sobre Pull Requests do Apêndice A
8
. Esse procedimento pode parecer um pouco estranho,
pois vo estará revisando um PR que você mesmo criou. Porém, nosso objetivo é apenas
simular, com fins didáticos, uma revisão de digo.
import java . util . ArrayList ;
import java . util . E mpt ySt ackEx cep tio n ;
public class Stack <T > {
private ArrayList <T > e lements = new ArrayList <T >();
public int size = 0;
8
../capAp.html#pull-requests
CAPÍTULO 21. REVISÃO DE CÓDIGO: UMA BREVE INTRODUÇÃO 107
public int size () {
return size ;
}
public bo olean isEmpty () {
return ( size == 0);
}
public void empilha (T elem ) {
elements . add ( elem );
size ++;
}
public T pop () throws Empty Sta ckE xce ption {
if ( isEmp ty ())
throw new E mpt yStac kEx cep tio n ();
T elem = elements . get ( size -1);
size - -;
return elem ;
}
}
4. É possível adotar simultaneamente Revisão de digo e Desenvolvimento Baseado no
Trunk (TBD)? Sim ou não? Justifique sua resposta. Se necessário, consulte a seguinte seção
9
do livro texto para saber mais sobre TBD.
5. É possível adotar simultaneamente Revisão de digo e Integração Contínua (CI)? Sim ou
não? Justifique sua resposta. Se necessário, consulte a seguinte seção
10
do livro texto para
saber mais sobre CI.
6. O seguinte link
11
contém o código de uma função de um sistema de digo aberto, chamado
FitNesse, a qual é usada também em um dos exemplos do livro Clean Code. Se você ficasse
encarregado de revisar o digo dessa função, qual a principal observação que você faria?
7. Como vo revisaria cada um dos trechos de digo a seguir?
(a)
const y yy ym mdstr = moment (). format (" YYYY /MM/ DD " );
(b)
ge tU se rInfo ();
ge tClient Data ();
ge tCu stome rRe cord ();
(c)
const l oc ations = [" Austin " , " New York ", " San Francis co " ];
locations . forEach (l = > {
doStuff ();
9
../cap10.html#desenvolvimento-baseado-no-trunk
10
../cap10.html#o-que-%C3%A9-integra%C3%A7%C3%A3o-cont%C3%ADnua
11
./exemplo-funcao-revisao-codigo.txt
CAPÍTULO 21. REVISÃO DE CÓDIGO: UMA BREVE INTRODUÇÃO 108
do SomeO the rStuf f ();
// ...
// ...
// ...
dispatch (l );
});
Esses exemplos são do seguinte repositório
12
que documenta boas práticas para escrita de
digo em JavaScript.
12
https://github.com/ryanmcdermott/clean-code-javascript
Capítulo 22
Como Monetizar Sistemas de
Software?
Este artigo didático oferece uma visão geral sobre modelos de monetização (ou precificação)
de produtos de software.
22.1 Licenças
A um tempo atrás, a principal maneira de comercializar um sistema de software que você
ou sua empresa desenvolveu e pretende agora lucrar com ele era por meio de uma licença
de uso.
Por exemplo, no início da empresa, a Microsoft somente vendia licenças de uso de seus
sistemas. Veja um exemplo de anúncio do sistema operacional Windows na próxima figura:
Figura 22.1: Exemplo de software comercializado por meio de licenças
Os usuários compravam licenças de sistemas como Windows e Office e, assim, ganhavam o
direito de instalá-los na sua máquina e receber gratuitamente atualizações. Basicamente,
essas atualizações consistiam em manutenções corretivas, com correções de bugs. Depois de
um certo tempo, a Microsoft criava novas versões de seus sistemas, com novas funcionalidades
e então os clientes tinham que adquirir novas licenças.
Licenças de uso podem ser individuais, para uso em um único computador, ou para uso em
mais de um computador. Além disso, algumas licenças podem implicar em alguma restrição
109
CAPÍTULO 22. COMO MONETIZAR SISTEMAS DE SOFTWARE? 110
ao funcionamento do sistema. Um bom exemplo são servidores de bancos de dados, onde o
cliente pode pagar pelo número de conexões simultâneas que serão aceitas pelo servidor.
22.1.1 Licenças & Serviços
Uma alternativa ao modelo de licenças puro consiste na aquisição de uma licença e também
na contração de serviços.
Dentre eles, podemos citar serviços de manutenção, incluindo manutenções corretivas e
também algumas manutenções adaptativas (por exemplo, criar um novo relatório ou integrar
o sistema licenciado a sistemas de terceiros). Particularmente, a contratação de serviços de
manutenção é mais comum no caso de sistemas de informações, como sistemas integrados de
gestão (ERPs).
Além de serviços de manutenção, podem ser firmados também contratos relativos a serviços
de treinamento de usuários, instalação e suporte.
22.2 SaaS: Software as a Service
Esse modelo popularizou-se com o surgimento da Web. A ideia é que os clientes não adquirem
mais uma licença que direito a uso do sistema pelo tempo que for necessário (ou então,
até que ele se torne obsoleto). Em vez disso, paga-se uma assinatura mensal (ou anual) para
a empresa que desenvolve e mantém o sistema. Mediante esse pagamento, o usuário ganha
um login e senha, que direito ao uso do sistema.
Portanto, o modelo SaaS requer que a empresa vendedora hospede e mantenha alguma
infraestrutura computacional para execução do sistema. Ou seja, os clientes não precisam
comprar servidores e software básico para rodar o sistema, pois eles são de responsabilidade
da própria empresa vendedora.
Atualmente, SaaS é o modelo predominante para monetização de sistemas de software, devido
à popularização da Web e de plataformas de computação em nuvem.
Para dar um exemplo de um sistema SaaS brasileiro, a próxima figura mostra os preços do
sistema de gestão financeira e contábil da empresa ContaAzul.
Veja que o sistema oferece diferentes planos de assinatura (Básico, Controle, Avançado, etc).
Cada plano possui um número máximo de usuários e um número máximo de notas fiscais que
podem ser emitidas por mês.
Podemos citar também diversos outros exemplos de sistemas comercializados no modelo SaaS:
sistemas de folhas de pagamento, como Gusto.
sistemas de gerenciamento clientes, como Salesforce.
Sistemas de escritório, como Office 365.
sistemas de comunicação, como Slack.
sistemas de compartilhamento de arquivos, como Dropbox.
sistemas de mail, como o GMail.
sistemas de conferência, como Zoom.
sistemas de pagamento, como Shopify.
sistemas de gerenciamento de projetos, como Basecamp.
sistemas de controle de versões, como GitHub e GitLab.
etc
CAPÍTULO 22. COMO MONETIZAR SISTEMAS DE SOFTWARE? 111
Figura 22.2: Exemplo de software comercializado no modelo de assinaturas
O leitor pode estar pensando que alguns desses serviços são gratuitos, como GMail, Dropbox
ou GitHub. No entanto, é comum que sistemas SaaS adotem uma estratégia de marketing
chamada de
freemium
. Nesse caso, o sistema possui uma versão aberta e gratuita, mas com
algumas limitações e uma versão paga, com um conjunto completo de funcionalidades. Veja
alguns exemplos:
O GMail é gratuito, mas se o usuário quiser suporte ou quiser ter endereços de mail
com um domínio próprio, o Google oferece um serviço pago.
O GitHub e GitLab são gratuitos apenas para repositórios públicos; repositórios privados
requerem o pagamento de uma assinatura.
O Dropbox é gratuito até um limite de armazenamento, de alguns gigabytes. Se o
usuário precisar de mais armazenamento, ele deve pagar.
Também existem empresas bem menores do que aquelas mencionadas acima e que sobrevivem
oferecendo sistemas SaaS. Algumas são constituídas por um único desenvolvedor. Elas
identificaram algum nicho de mercado, desenvolveram um sistema para atuar nesse nicho
e então possuem algumas centenas de usuários, no máximo milhares, que pagam valores
relativamente baixo de assinaturas.
22.3 Anúncios
No mundo da Web, existe uma máxima que diz que quando você usa “um sistema gratuito,
vo não é um usuário, mas sim um produto”. Atualmente, essa frase aplica-se perfeitamente
a grandes redes sociais, como Facebook, Instagram e Twitter; e também a serviços de busca,
como o Google. Todos eles são serviços “gratuitos”, no sentido de que vo não precisa pagar
uma assinatura, como no caso de sistemas SaaS.
O modelo de monetização desses sistemas depende da atração de um grande número de
usuários, da coleta de dados sobre o perfil e as preferências deles, e então da oferta de anúncios.
CAPÍTULO 22. COMO MONETIZAR SISTEMAS DE SOFTWARE? 112
Pode-se também combinar modelos de monetização. Por exemplo, a versão gratuita de um
sistema SaaS pode incluir anúncios. No entanto, ao realizar uma assinatura, o usuário fica
livre dos anúncios e, possivelmente, tem acesso a mais funcionalidades.
22.4 Open Source
Este é o tipo de sistema mais desafiador de se monetizar. O motivo é que, por definição,
sistemas de digo aberto podem ser livremente copiados, modificados e instalados, sem
pagamento de nenhum valor monetário para o autor do digo.
No entanto, mesmo no caso de sistemas de digo aberto, existem pelo menos duas formas de
monetização:
Por meio de doações dos usuários. Por exemplo, o sistema operacional Linux é mantido
por uma fundação, sem fins lucrativos, que sobrevive graças a contribuições de grandes
empresas usuárias do sistema operacional. Outros exemplos são a Fundação Mozilla
e a Fundação Apache. No entanto, nem sempre é necessário criar uma fundação para
receber doações. Muitas vezes, elas são feitas diretamente para os mantenedores do
sistema de digo aberto, por meio de plataformas de captação de recursos, como
Patreon. Mais recentemente, o GitHub está também habilitando um serviço de doações.
Por meio de prestação de serviços relacionados com o sistema de digo aberto, incluindo
treinamento e customizações. Um exemplo de empresa que adotou esse padrão foi a
Red Hat, que oferece serviços de suporte e consultoria para empresas que usam Linux.
22.5 Conclusão
Neste artigo, apresentamos os modelos que vo pode usar para monetizar um software que
vo ou sua empresa pretende desenvolver. Faltou, no entanto, comentar sobre como definir
realmente o preço da licença, assinatura, dos serviços associados, etc. Para não deixar essa
questão completamente em aberto, vamos rascunhar uma resposta nesta conclusão.
O esboço de resposta a seguir aplica-se a principalmente a desenvolvedores independentes
que não querem depender de financiamento externo. Neste caso, a definição do preço de um
produto de software depende das seguintes variáveis principais:
Custos de desenvolvimento do produto, incluindo salários, computadores, conexão de
Internet, aluguel de serviços de cloud, propaganda, marketing, etc.
Capital disponível para desenvolvimento e operação do sistema. Ou seja, se você dispõe
de um capital inicial pequeno, você não deve tentar construir um sistema grande e
complexo, pois irá gastar esse capital completamente antes de ter um produto renvel.
Preços praticados por sistemas concorrentes, além da capacidade de pagamento dos
potenciais clientes do seu sistema. Por exemplo, se seu mercado são pequenas empresas,
vo não pode cobrar uma assinatura que não esteja ao alcance delas. Ou então
cobrar uma assinatura muito acima de sistemas concorrentes, que resolvem problemas
semelhantes aos do seu sistema.
Para concluir mesmo, deve-se pensar também no
valor
de um sistema. Basicamente, preço é
o que o desenvolvedor vai cobrar pelo sistema e que o cliente vai pagar por ele. Valor é o
benefício ou a economia que o cliente vai obter com o uso do sistema. Por exemplo, suponha
que os clientes após adotarem um certo sistema vão conseguir um aumento de R$ 100
mil nas suas vendas anuais. Esse número é então o valor do sistema para seus clientes. Por
CAPÍTULO 22. COMO MONETIZAR SISTEMAS DE SOFTWARE? 113
isso, e se essa estimativa de valor estiver mesmo correta, os clientes provavelmente estarão
dispostos a pagar R$ 10 mil de assinatura anual pelo sistema, mesmo que custos dele (para
quem o desenvolve) sejam muito menores do que esse montante. Porém, estimar o valor de
um sistema costuma não ser trivial. Pelo menos, essa tarefa tende a ser mais difícil do que a
estimativa dos seus custos. Além disso, o valor de um sistema pode variar de cliente para
cliente.
Artigo Relacionado
Veja também este outro artigo
1
sobre microempresas de software como serviço.
Exercícios
1. Pesquise sistemas de empresas brasileiras que adotam o modelo SaaS. O que esses sistemas
fazem? Qual o valor da assinatura cobrada? Eles adotam o modelo freemium?
2. São vantagens de sistemas SaaS, EXCETO:
(a)
Clientes não precisam ter o hardware adequado para executar a funcionalidade principal
de um sistema SaaS.
(b) Clientes não precisam realizar backups dos seus dados.
(c)
Clientes são imunes a falhas de segurança, pois sistemas SaaS executam em plataformas
de computação em nuvem.
(d)
Atualizações e correções de bugs são mais fáceis, pois elas ocorrem apenas nos servidores.
(e)
Clientes não precisam comprar uma nova licença quando uma versão importante do
sistema for lançada.
3. Defina: (a) custo de um sistema; (b) valor de um sistema.
1
./micro-saas.html
Capítulo 23
Microempresas de Software como
Serviço
23.1 Introdução
Existem diversos caminhos na área de empreendedorismo envolvendo Engenharia de Software.
Hoje em dia, talvez, o caminho mais desejado seja criar uma startup de sucesso, obter
financiamento via capital de risco e virar um unicórnio. No entanto, esse não é um caminho
trivial, pois são pouquíssimas empresas que alçam esse nível de sucesso. Além disso, existe
uma vasta literatura sobre a criação e administração de startups financiadas por meio de
capital de risco.
Por outro lado, existe um caminho oposto, que se costuma chamar de autofinanciado (boots-
trapped), no qual a empresa é criada com recursos próprios e mantida com os resultados do
seu faturamento. Como ele é menos comentado, pelo menos na literatura, resolvemos escrever
este pequeno artigo sobre o assunto.
Quando adotam um modelo de assinatura, essas empresas são chamadas de
micro-SaaS
.
Podemos definir esse conceito da seguinte forma:
Micro-SaaS é um pequeno empreendimento de software, autofinan-
ciado, administrado por um ou dois desenvolvedores e que atende
a uma necessidade específica. Um micro-SaaS segue um modelo
de serviços (Software as a Service), com pagamentos recorrentes
(assinaturas) por parte de centenas ou alguns milhares de clientes.
23.2 Exemplos de Micro-SaaS
Para ficar mais claro, vamos dar alguns exemplos de sistemas micro-SaaS:
Saber Feedback
1
é uma aplicação que possibilita adicionar um pequeno botão “Feedback”
na lateral direita de todas as páginas de um site Web. Usuários podem usar esse botão
para dar feedback sobre o site e suas páginas, reportando desde erros ortográficos até
bugs de lógica ou de interface. O sistema é mantido por um único fundador, mais alguns
freelancers.
1
https://saberfeedback.com/
114
CAPÍTULO 23. MICROEMPRESAS DE SOFTWARE COMO SERVIÇO 115
Transistor
2
é um sistema para hospedagem e distribuição de podcasts, que é mantido
por dois fundadores.
Permanentlink
3
é um sistema que se propõe a resolver um problema enfrentado por
autores de livros digitais: esses livros incorporam diversos links que com o tempo podem
“quebrar”. A ideia então é disponibilizar uma URL permanente para os documentos
referenciados. Enquanto o link estiver ativo, o sistema redireciona a conexão para ele;
se o link quebrar, retorna-se uma cópia da página armazenada localmente pelo sistema.
Reform
4
é um sistema para criação de formulários. Ou seja, o sistema tem que competir
com concorrentes importantes, tal como sumarizado na seguinte página
5
da Wikipédia.
Apesar disso, os fundadores estão tentando criar um sistema alternativo que propicie
uma experiência mais agradável para criação e resposta de formulários.
De forma interessante, esses sistemas são tanto B2B (Business-to-Business) como B2C
(Business-to-Customers). Isto é, eles podem ter clientes que são empresas ou consumidores
finais. No Brasil, o mercado em ambos os casos é muito grande, pois o país possui mais de
210 milhões de habitantes e quase 20 milhões de empresas ativas (dados de 2020).
23.3 Escolhendo o Problema
O grande
desafio de uma microempresa de software é achar um segmento de
mercado
que tenha um problema real que possa ser resolvido por meio de um sistema
relativamente pequeno. E, mais importante, esse problema tem que ser relevante para
atrair
clientes dispostos a pagar uma assinatura mensal
para ter acesso a um sistema que o
resolva.
Os resultados de uma pesquisa realizada em 2020 pela MicroConf, uma empresa organizadora
de eventos, ajudam a entender como a escolha desse problema e sistema ocorre na prática.
Nessa pesquisa, foram entrevistados fundadores de SaaS autofinanciados. Uma das perguntas
era a seguinte:
Como você teve a ideia para o seu produto ou empresa?
Veja as
respostas:
45% responderam que escolheram influenciados por um problema que estavam enfren-
tando.
22% responderam que escolheram influenciados por um problema que seus clientes ou
consumidores enfrentavam.
13% responderam que escolheram o problema baseado em experiências obtidas no seu
trabalho diário.
11% responderam que amigos ou parentes estavam enfrentando o problema.
O seguinte padrão fica então claro nas respostas: a escolha foi de um problema que os
fundadores ou pessoas próximas estavam enfrentando. Portanto, os fundadores conheciam
bem o problema e também tinham acesso a pessoas que poderiam validar ou mesmo serem
clientes das primeiras versões do sistema.
2
https://transistor.fm/
3
https://permanent.link/
4
https://www.reform.app/
5
https://en.wikipedia.org/wiki/Comparison_of_survey_software
CAPÍTULO 23. MICROEMPRESAS DE SOFTWARE COMO SERVIÇO 116
Os resultados da pesquisa conectam-se com duas expressões usadas com frequência em
desenvolvimento de software:
Scrath your own itch, que pode ser traduzida como “coçar a sua própria coceira”. Ou
seja, empreendedores devem escolher sistemas que vão resolver problemas que eles
também estão enfrentando.
Eat your own dog food, que pode ser traduzida como “comer a mesma comida do seu
cachorro”. Ou seja, empreendedores devem ser também usuários dos sistemas que
desenvolvem.
23.4 Canais de Distribuição
De nada adianta ter uma excelente ideia e não ter capacidade de execução, isto é, capacidade
de concretizar sua ideia em um produto de software que não tenha bugs, que tenha uma
excelente interface, um desempenho muito bom e que não tenha brechas de segurança e
privacidade.
Porém, também de nada adianta ter um excelente produto se ninguém tomar conhecimento
dele. Na verdade, a premissa de que um “produto bom se vende sozinho” costuma ser falsa.
Logo, é fundamental planejar, testar e orçar os
canais de distribuição
que pretende usar
para divulgar o seu micro-SaaS. Existem dois tipos principais de canais de distribuição:
Canais outbound, por meio dos quais vo procura os clientes ativamente. Por exemplo,
comprando anúncios no Google, Facebook, Instagram, etc. Embora menos comum
no caso de pequenos sistemas, pode-se também contratar vendedores para entrar em
contato com possíveis clientes do seu micro-SaaS.
Canais inbound, por meio dos quais o cliente descobre seu sistema e site. Isso acontece
principalmente quando você investe em marketing de conteúdo, por exemplo, criando
um blog com artigos sobre o problema resolvido pelo seu sistema. Os clientes podem
então chegar a seus artigos ao pesquisarem no Google. Além de blogs, é possível gerar
conteúdo criando podcasts, canais no YouTube, e-books, etc.
Adicionalmente, um modelo freemium também pode ajudar a conseguir clientes, assumindo
que eles tomaram conhecimento do seu produto e visitaram o seu site. Nesse modelo, os
clientes podem testar o sistema por um tempo de forma gratuita.
Resumindo, ao montar um micro-SaaS, vo deve:
1.
Focar em um problema real que afete um número relevante de usuários (seu mercado).
2.
Em seguida, você deve implementar o melhor sistema que resolva esse problema (seu
produto);
3.
E também deve encontrar os melhores canais de distribuição que vão permitir que o
mercado tome conhecimento do seu produto.
23.5 Outras Responsabilidades
Além de construir, manter e vender o produto, os fundadores de uma microempresa de
software são responsáveis por outras funções, tais como:
CAPÍTULO 23. MICROEMPRESAS DE SOFTWARE COMO SERVIÇO 117
Cuidar da parte de operação. No entanto, essa tarefa ficou mais simples devido à
disponibilidade de sistemas de computação em nuvem que permitem criar máquinas
virtuais com todo o software básico instalado.
Cuidar da parte de cobrança e recebimento das assinaturas. Porém, hoje também
existem serviços de terceiros para isso.
Cuidar da parte de atendimento e suporte a clientes. Para isso, é importante automatizar
e documentar todos os processos e, também, usar sistemas de low-code, como Zapier.
23.6 Crescendo a Empresa
Quando uma microempresa de software faz sucesso e atrai um número maior de clientes, é
natural que o empreendimento cresça, principalmente na parte de atendimento a clientes. E
isso também pode ocorrer de forma autofinanciada, com os recursos das próprias assinaturas
do serviço.
Um caso interessante é o sistema Less Annoying CRM
6
, para gerenciamento de dados de
clientes. Como o próprio nome diz, seu objetivo é ser um sistema de CRM (Customer
Relationship Management) mais simples do que os sistemas concorrentes de grandes empresas,
como Salesforce. Em contrapartida, a assinatura do sistema é também mais barata. A Less
Annoying CRM foi criada em 2009, por dois fundadores. Em 2013, a empresa alcançou 1.5K
clientes, com apenas mais um colaborador. Em 2021, ela possui quase 24 mil clientes e 19
colaboradores, a maior parte atuando em atendimento a clientes.
Um segundo exemplo de empresa de SaaS que começou pequena e cresceu bastante, mas
mantendo o modelo de autofinanciamento, é a Balsamiq
7
, que faz uma ferramenta para criação
de wireframes de baixa fidelidade. A empresa, localizada na Itália, foi fundada em 2008 e, em
2020, obteve um faturamento de US$ 7.3 milhões, com 33 colaboradores.
23.7 Métricas
Antes de concluir, vamos comentar sobre algumas métricas para avaliar o sucesso de sistemas
SaaS:
Custo de Aquisição de Clientes (CAC)
(ou Customer Acquisition Cost): custos
com propaganda, vendas, etc em um mês / número de novos assinantes no mês.
Receita Mensal Recorrente (MRR)
(ou Monthly Recurring Revenue): somatório
do valor das assinaturas recebidas no mês. Ou seja, é o faturamento mensal obtido com
o sistema. Existe também o termo Receita Anual Recorrente (ARR).
Taxa de Cancelamentos
(Churn Rate): número de cancelamentos no mês / total de
clientes no início do mês.
Valor do Tempo de Vida do Cliente
(LTV) (Customer Lifetime Value): assinatura
média no mês / taxa de cancelamentos. Exemplo: se a assinatura média é de R$ 100 e
a taxa de cancelamento for de 25%, temos que LTV = 100 / 0.25 = R$ 400.
Para um SaaS ser viável, LTV > CAC. Na verdade, costuma-se recomendar que LTV / CAC
> 3.0.
6
https://www.lessannoyingcrm.com
7
https://balsamiq.com
CAPÍTULO 23. MICROEMPRESAS DE SOFTWARE COMO SERVIÇO 118
Uma outra métrica muito usada para avaliar a satisfação de sistemas SaaS é o
Net Promoter
Score (NPS)
. Para calcular essa métrica, precisamos fazer a seguinte pergunta para um
conjunto de clientes:
Em uma escala de 0 a 10, qual a chance de você recomendar nosso
sistema para um amigo?
Então, o NPS é assim calculado:
NPS = % de respostas 9 e 10 (promotores) menos o % de respostas
0 a 6 (detratores).
Logo, se uma empresa, serviço ou sistema tem 70% de promotores e 10% de detratores, seu
NPS será 60 (70-10). Veja que as respostas 7 e 8 são consideradas neutras e, por isso, não
entram no cálculo do NPS.
Valores de NPS acima de 50 são considerados bons. Por exemplo, grandes empresas da
Internet costumam ter NPS próximo de 60 ou até mesmo um pouco inferior.
O NPS é importante porque se um empresa tem um NPS alto então o seu CAC e Churn
tendem a ser menores. E, consequentemente, o LTV será maior.
Artigo Relacionado
Veja também este outro artigo
8
sobre monetização de software.
Exercícios
1. Do ponto de vista de monetização, qual a vantagem de micro-SaaS em relação a uma
pequena app não-SaaS comercializada em lojas como Google Play e Apple Store?
2. Define-se como “churn de receita negativo” a situação na qual o novo faturamento obtido
com os clientes atuais de um sistema supera a perda de faturamento devido aos cancelamentos
de outros clientes. Por exemplo, um SaaS tinha 100 clientes e faturava R$ 10 mil/mês. Em um
dado mês, ele perdeu 10 clientes, porém os 90 clientes que ficaram geraram um faturamento
de R$ 11 mil. Em outras palavras, tivemos uma perda negativa de receita, ou seja, um ganho
de receita. Explique como isso é possível. Quais eventos podem ter sido responsáveis por essa
situação?
3. Leia e faça um breve resumo do seguinte artigo: Do Things that Don’t Scale (link
9
), de
autoria de Paul Graham (2013). No seu resumo, liste algumas tarefas mencionadas no artigo
e que, exatamente por não escalarem, devem ser feitas por fundadores nos estágios iniciais de
criação de uma empresa de software.
4. Qual o maior e o menor valor possível para a métrica NPS? Em quais situações eles
ocorrem?
5. Pesquise e descreva pelo menos um sistema micro-SaaS, de forma semelhante àquela que
fizemos no artigo.
8
./como-monetizar.html
9
http://paulgraham.com/ds.html
CAPÍTULO 23. MICROEMPRESAS DE SOFTWARE COMO SERVIÇO 119